gpt4 book ai didi

c# - DDD 富领域模型和聚合根

转载 作者:行者123 更新时间:2023-11-30 23:19:30 24 4
gpt4 key购买 nike

我正在尝试开发一个允许用户为 Multi-Tenancy 应用程序创建租户的小型应用程序。在生产服务器上,我们有 2 个站点实例,版本 N(生产)和版本 N + 1(测试生产)。

一些租户是测试租户。所以我们必须可以更改站点版本的租户。

将租户分配给站点后,我需要使用 API Microsoft.Web.Administration 配置 IIS 以将绑定(bind)添加到站点实例。

示例:如果我们将租户 (tenant1) 从生产环境转移到测试生产环境,我们必须删除站点“production”的绑定(bind)“www.tenant1.com”并将其添加到站点“production-test”

对于这个域,我设计了两个聚合根

public class Tenant : IEntity, IAggregateRoot
{
public int Id { get; set; }
public string Name { get; set; }
public string DbInstanceName { get; set; }
public Site Site { get; set; }
public virtual Icollection<Binding> Bindings { get; set; }
}

public class Binding: IEntity
{
public int Id { get; set; }
public int Port { get; set; }
public string Protocol { get; set; }
public string Adress { get; set; }
}

当我加载一个租户时,它的绑定(bind)被加载,网站正在加载但不是网站的绑定(bind)..

public class Site : IEntity, IAggregateRoot
{
public int Id { get; set; }
public string Name { get; set; }
public string IISiteName { get; set; }

public virtual ICollection<Tenant> Tenants { get; set; }
public virtual ICollection<Binding> Bindings { get; set; }
}

当我加载一个网站时,它的绑定(bind)被加载,租户正在加载但不是租户的绑定(bind)..

首先,在站点中有租户列表,在租户中有站点,知道租户和站点是聚合是可以接受的吗?

然后我有一个用例给我带来了问题,因为允许用户编辑租户的 View 包含一个网站组合,当租户更新时可以更改与网站的关联。

第一种方法:

public class TenantService {
public void UpdateTenant(Tenant tenant, int newSiteId) {

var currentTenant = _repoTenant.find(tenant.Id);
var newSite = _repoSite.find(newSiteId);

// mapping
// ...

if(tenant.Site != null) {
// important: i have to load site from it's aggregate, because I KNOW (but if i was another developer i wouldn't)
// that's site's bindings are note loaded from client's aggregate
var currentSite = _repoSite.find(tenant.Site);
currentSite.RemoveTenant(tenant);
// iisadministrator, supply an abstraction to configure binding on iis
iisadministrator.SetBinding(currentSite .IISSiteName, currentSite.Bindings);
}

newSite.Add(tenant);
// iisadministrator, supply an abstraction to configure binding on iis
iisadministrator.SetBinding(newSite.IISSiteName, newSite.Bindings);

// saving..
}
}

public class Site : IEntity, IAggregateRoot
{
public void AddTenant(Tenant tenant) {
this.Tenants.Add(tenant);
tenants.Bindings.ToList().Foreach( b => this.Bindings.Add(b));
}

public void RemoveTenant(Tenant tenant) {
this.Tenants.Remove(tenant);
tenants.Bindings.ToList().Foreach( b => this.Bindings.Remove(b));
}
}

这种方法的 3 个问题:

  • 当另一个开发人员向站点添加租户时,我不能确定他之前是否已从其旧站点中删除
  • 当开发人员将租户添加到站点时,我无法确定他更新了 IIS 上的绑定(bind)
  • 可能是建模问题:开发人员必须知道它必须完全加载当前站点(拥有所有绑定(bind)并可以更新 iis)第二个:
public class TenantService {
public void UpdateTenant(Tenant tenant, int newSiteId) {

var currentTenant = _repoTenant.find(tenant.Id);
var newSite = _repoSite.find(newSiteId);

// mapping
// ...

// iisadministrator, supply an abstraction to configure binding on iis
newSite.Add(client, iisAdministrator);

// saving..
}
}

public class Site : IEntity, IAggregateRoot
{
public void AddTenant(Tenant tenant, IISAdministrator iisadministrator) {

if(tenant.Site == Site) return;


if(tenant.Site != null) tenant.Site.removeTenant(tenant); <-- all bindings are not loaded (1)

this.Tenants.Add(tenant);
tenants.Bindings.ToList().Foreach( b => this.Bindings.Add(b));
iisadministrator.SetBinding(this.IISSiteName, this.Bindings);
}

public void RemoveTenant(Tenant tenant, IISAdministrator iisadministrator) {
this.Tenants.Remove(tenant);
tenants.Bindings.ToList().Foreach( b => this.Bindings.Remove(b));
iisadministrator.SetBinding(this.IISSiteName, this.Bindings); <-- all bindings are not loadedn see(1)
}
}

更好,但这里还有其他问题:

  • 当前Site的所有绑定(bind)都没有加载,所以更新iis时有问题

同样在2种情况下,如何在同一个事务中进行实体的更改和IIS的更改?

关于建模,如何取舍:

tenant.setSite(Site site) { }
tenant.changeSite(Site oldSite, Site newSite) { }
site.AddTenant(Tenant tenant) { }

是否有相应的方法?

谢谢。

最佳答案

First, it is acceptable to have a tenant list in the site, and site in a tenant, knowing that tenant and site are aggregates?

不,这没有任何意义。引用列表可能有意义,具体取决于您需要每个聚合执行的不变量。尝试将一个聚合嵌套在另一个聚合中表明您的聚合边界出现了严重错误。

how to make change of entities and changes of IIS in the same transaction?

好吧,您可以四处寻找管理两阶段提交的方法,但通常的答案是在与更新模型不同的单独事务中将消息发送到您的端口。您通常会放弃防止模型和远程系统不同步的想法,而是专注于检测和缓解。

参见 Udi Dahan 在 reliable messaging 上的演讲. “Setter”通常是幂等的,因此至少一次交付可能会产生令人满意的结果。

About modeling, how to choose between:

tenant.setSite(Site site) { }
tenant.changeSite(Site oldSite, Site newSite) { }
site.AddTenant(Tenant tenant) { }

is there a methodology for that?

一般规则是“set”应该被认为是一种代码味道;把业务逻辑放到聚合中,让它做判断。如果没有做出判断——那么为什么该数据成员根本不是聚合的一部分?

或者,换句话说,您需要评估您的解决方案是数据库还是服务。 Borrowing from Udi again ...

Dahan considers that a service has to have both some sort of functionality and some data. If it does not have data, then it is just a function. If all that it does is performing CRUD operations on data, then it is database.

如果您的解决方案不能否决更改,如果它的职责仅限于记录在其他地方做出的业务决策,那么您应该考虑数据库。

关于c# - DDD 富领域模型和聚合根,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/40237752/

24 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com