C# DDD丰富域模型和聚合根

C# DDD丰富域模型和聚合根,c#,domain-driven-design,C#,Domain Driven Design,我正在尝试开发一个小应用程序,允许用户为多租户应用程序创建租户。 在生产服务器上,我们有两个站点实例,一个版本N(生产)和一个版本N+1(测试生产) 有些租户是测试租户。所以我们必须能够改变一个租户的网站版本 将租户分配给站点时,我需要使用API Microsoft.Web.Administration配置IIS,以向站点实例添加绑定 示例:如果我们将租户(tenat1)从生产传递到测试生产,则必须删除站点“production”的绑定“www.tenat1.com”,并将其添加到站点“prod

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

有些租户是测试租户。所以我们必须能够改变一个租户的网站版本

将租户分配给站点时,我需要使用API Microsoft.Web.Administration配置IIS,以向站点实例添加绑定

示例:如果我们将租户(tenat1)从生产传递到测试生产,则必须删除站点“production”的绑定“www.tenat1.com”,并将其添加到站点“production test”中

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

公共类租户:IEntity,IAggregateRoot
{
公共int Id{get;set;}
公共字符串名称{get;set;}
公共字符串DbInstanceName{get;set;}
公共站点站点{get;set;}
公共虚拟Icollection绑定{get;set;}
}
公共类绑定:通用性
{
公共int Id{get;set;}
公共int端口{get;set;}
公共字符串协议{get;set;}
公共字符串地址{get;set;}
}
当我加载租户时,它的绑定被加载,站点被加载,但不是站点的绑定

公共类站点:IEntity,IAggregateRoot
{
公共int Id{get;set;}
公共字符串名称{get;set;}
公共字符串IISiteName{get;set;}
公共虚拟ICollection租户{get;set;}
公共虚拟ICollection绑定{get;set;}
}
当我加载一个站点时,它的绑定被加载,租户正在加载,而不是租户的绑定

首先,在知道承租人和站点是聚合的情况下,站点中有承租人列表,站点中有承租人列表是可以接受的

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

第一种方法:

公共类租户服务{
public void UpdateTenant(租户,int newSiteId){
var currentTenant=\u repoTenant.find(tenant.Id);
var newSite=_repoSite.find(newSiteId);
//映射
// ...
如果(tenant.Site!=null){
//重要提示:我必须从它的聚合中加载站点,因为我知道(但如果我是另一个开发人员,我不会)
//也就是说,站点的绑定是从客户端的聚合中加载的
var currentSite=_repoSite.find(tenant.Site);
当前站点。移除承租人(承租人);
//iisadministrator,提供一个抽象来配置iis上的绑定
iisadministrator.SetBinding(currentSite.IISSiteName,currentSite.Bindings);
}
newSite.Add(租户);
//iisadministrator,提供一个抽象来配置iis上的绑定
iisadministrator.SetBinding(newSite.IISSiteName,newSite.binding);
//拯救。。
}
}
公共类站点:IEntity,IAggregateRoot
{
公共无效添加租户(租户){
this.Tenants.Add(租户);
Foreach(b=>this.Bindings.Add(b));
}
公共无效移除承租人(承租人){
此。租户。移除(租户);
Foreach(b=>this.Bindings.Remove(b));
}               
}
此方法的3个问题:

  • 当另一个开发者向一个站点添加一个租户时,我不能确定他以前是否已经从它以前的站点移除了
  • 当开发人员将租户添加到站点时,我不能确定他是否更新了IIS上的绑定
  • 可能是建模问题:开发人员必须知道必须完全加载当前站点(才能拥有所有绑定并可以更新iis) 第二个:
公共类租户服务{
public void UpdateTenant(租户,int newSiteId){
var currentTenant=\u repoTenant.find(tenant.Id);
var newSite=_repoSite.find(newSiteId);
//映射
// ...
//iisadministrator,提供一个抽象来配置iis上的绑定
添加新闻站点(客户、IISA管理员);
//拯救。。
}
}
公共类站点:IEntity,IAggregateRoot
{
公共无效添加租户(租户租户、IISAdministrator、IISAdministrator){
如果(tenant.Site==Site)返回;
如果(tenant.Site!=null)tenant.Site.removeTenant(tenant);this.Bindings.Add(b));
iisadministrator.SetBinding(this.IISSiteName,this.Bindings);
}
公共无效清除承租人(承租人-承租人,IISA管理员IISA管理员){
此。租户。移除(租户);
Foreach(b=>this.Bindings.Remove(b));
iisadministrator.SetBinding(this.IISSiteName,this.Bindings);
首先,在知道承租人和站点是聚合的情况下,站点中有承租人列表,站点中有承租人列表是可以接受的

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

如何在同一事务中更改实体和IIS

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

参见UDIDahan关于“Setters”的讨论,“Setters”通常是幂等的,因此至少一次传递可能会产生令人满意的结果

关于建模,如何选择:

有没有一种方法

一般规则是,“set”应该被视为一种代码味道;将业务逻辑放入聚合中,让它做出判断
tenant.setSite(Site site) { }
tenant.changeSite(Site oldSite, Site newSite) { }
site.AddTenant(Tenant tenant) { }