我有一个常见的场景,我正在寻找一些有DDD和域建模经验的人的指导.
假设我开始构建一个博客引擎,第一个要求是在发布文章后,用户可以开始在其上发布评论.这开始很好,并导致以下设计:
public class Article
{
public int Id { get; set; }
public void AddComment(Comment comment)
{
// Add Comment
}
}
Run Code Online (Sandbox Code Playgroud)
我的MVC控制器设计如下:
public class ArticleController
{
private readonly IRepository _repository;
public ArticleController(IRepository repository)
{
_repository = repository;
}
public void AddComment(int articleId, Comment comment)
{
var article = _repository.Get<Article>(articleId);
article.AddComment(comment);
_repository.Save(article);
return RedirectToAction("Index");
}
}
Run Code Online (Sandbox Code Playgroud)
现在一切正常,它符合要求.下一次迭代我们得到一个要求,即每次发布评论时,博客作者都应该收到一封电子邮件通知他.
在这一点上,我有两个我能想到的选择.1)修改文章以要求IEmailService(在ctor?中)或从静态引用到我的DI容器获取EmailService
1a)看起来很难看.我相信它破坏了我的实体知道服务的一些域模型规则?
public class Article
{
private readonly IEmailService _emailService;
public Article(IEmailService emailService)
{
_emailService = emailService;
}
public void AddComment(Comment comment) …Run Code Online (Sandbox Code Playgroud) 危险......危险史密斯博士......前面的哲学文章
这篇文章的目的是确定将验证逻辑放在我的域实体之外(实际上是聚合根)是否实际上给了我更大的灵活性或它是神风代码
基本上我想知道是否有更好的方法来验证我的域实体.这就是我计划这样做的方式,但我希望你的意见
我考虑的第一种方法是:
class Customer : EntityBase<Customer>
{
public void ChangeEmail(string email)
{
if(string.IsNullOrWhitespace(email)) throw new DomainException(“...”);
if(!email.IsEmail()) throw new DomainException();
if(email.Contains(“@mailinator.com”)) throw new DomainException();
}
}
Run Code Online (Sandbox Code Playgroud)
我实际上不喜欢这种验证,因为即使我将验证逻辑封装在正确的实体中,这也违反了Open/Close原则(Open for extension但Close for modification)我发现违反这个原则,代码维护就变成了当应用程序在复杂性中成长时,真正的痛苦.为什么?因为域规则的变化比我们想要承认的更频繁,并且如果规则被隐藏并嵌入到这样的实体中,它们很难测试,难以阅读,难以维护但是我不喜欢这个的真正原因方法是:如果验证规则发生变化,我必须来编辑我的域实体.这是一个非常简单的例子,但在RL中验证可能更复杂
因此遵循Udi Dahan的哲学,明确角色,以及Eric Evans在蓝皮书中的推荐,接下来的尝试是实现规范模式,就像这样
class EmailDomainIsAllowedSpecification : IDomainSpecification<Customer>
{
private INotAllowedEmailDomainsResolver invalidEmailDomainsResolver;
public bool IsSatisfiedBy(Customer customer)
{
return !this.invalidEmailDomainsResolver.GetInvalidEmailDomains().Contains(customer.Email);
}
}
Run Code Online (Sandbox Code Playgroud)
但后来我才意识到,为了遵循这种方法,我必须首先改变我的实体,以便传递值为valdiated,在这种情况下是电子邮件,但是改变它们会导致我的域事件被触发,我不想直到新电子邮件有效为止
因此,在考虑了这些方法后,我推出了这个方法,因为我要实现CQRS架构:
class EmailDomainIsAllowedValidator : IDomainInvariantValidator<Customer, ChangeEmailCommand>
{
public void IsValid(Customer entity, ChangeEmailCommand command)
{
if(!command.Email.HasValidDomain()) throw new DomainException(“...”);
} …Run Code Online (Sandbox Code Playgroud) 为了利用java.util.streamJdk 8中包含的各种查询方法,我尝试设计域模型,其中具有*多重性(具有零个或多个实例)的关系的getter 返回a Stream<T>而不是Iterable<T>or Iterator<T>.
我怀疑的是,与此Stream<T>相比,Iterator<T>是否会产生任何额外的开销?
那么,使用Stream<T>?来破坏我的域模型是否有任何不利之处?
或者,我应该总是返回一个Iterator<T>或者Iterable<T>,并通过将该迭代器转换为StreamUtils?来让最终用户决定是否使用流.
请注意,返回a Collection不是有效选项,因为在这种情况下,大多数关系都是惰性且大小未知.
我刚刚学习DDD(Eric Evans的书在我面前开放),我遇到了一个我无法找到答案的问题.当您只是想获得一个简单的查找记录列表时,您在DDD中做了什么?
防爆.
EmployeeID:123
EmployeeName:John Doe
State:Alaska(下拉列表)
县:Wasilla(下拉 - 将根据州进行过滤).
例如,假设您有一个Employee域对象,一个IEmployeeRepository接口和一个EmployeeRepository类.UI将使用它来显示员工列表和个人详细信息.在UI中,您希望使用员工所在州和郡的下拉列表.将根据选择的状态筛选可用县.
不幸的是,数据库表和UI看起来非常不同.在tblEmployees中,它包含州代码= AK和县代码= 02130,而不是州和县名称.
旧的方式(在我开始此DDD任务之前)将非常简单,只需创建2个查询并使用DataReader填充下拉列表.下拉列表中显示的下方是值,它会自动用于表单帖子.
但是,对于DDD,我不确定你应该怎么做.我首先开始创建State和County对象以及存储库的存储库和接口.但是,编写4个类+2个接口以及hbm.xml文件和Employee Business对象中的管道对于2个下拉列表的2个查询来说似乎有些过分.必须有更好的方法,不是吗?我不会很快改变州或县表中的记录,即使我这样做,也不会通过这个应用程序.因此,如果我不需要,我真的不想为State和County创建业务对象.
我看到的最简单的解决方案是使用返回字典的方法创建一个辅助类,例如GetStatesAll(),GetState()和GetCounties()以及GetCounty(),但从DDD的角度来看,这感觉不对.
请帮忙.如何在没有过度设计的情况下使用DDD只需几个简单的查找?
最终解决方案 我认为我终于通过经验找到了答案,即将GetStates()方法放入自己的Data Access类中,尽管不是存储库类.由于我只进行只读访问,因此我将其放入结构DTO中.由于数据库很小,我把它们完全扔进了一个类,就像下面描述的Todd一样.
我的结论:
(注意:我的问题与三个月前提出这个问题的人非常相似,但从未得到过回答.)
我最近开始使用MVC3 + Entity Framework,我一直在阅读最佳实践是使用存储库模式集中访问DAL.这也伴随着您希望将DAL与域(尤其是视图层)分开的解释.但是在示例中,我看到存储库(或似乎是)只是返回DAL实体,即在我的情况下,存储库将返回EF实体.
所以我的问题是,如果它只返回DAL实体,那么存储库有什么用呢?这是否会增加一层复杂性,而不会消除在层之间传递DAL实体的问题?如果存储库模式创建"进入DAL的单一入口点",那么它与上下文对象有何不同?如果存储库提供了一种检索和持久化DAL对象的机制,那么它与上下文对象有何不同?
此外,我至少读过一个地方,工作单元模式集中了存储库访问以管理数据上下文对象,但我不知道为什么这也很重要.
我98.8%肯定我在这里遗漏了一些东西,但从我的读数中我没有看到它.当然,我可能只是没有阅读正确的消息来源......:
design-patterns domain-driven-design entity-framework repository-pattern
我的存储库处理并为丰富的域模型提供持久性.我不想将贫血的Entity Framework数据实体暴露给我的业务层,所以我需要一些在它们之间进行映射的方法.
在大多数情况下,从数据实体构造域模型实例需要使用参数化构造函数和方法(因为它很丰富).它不像属性/字段匹配那么简单.AutoMapper可用于相反的情况(映射到数据实体),但不能用于创建域模型.
以下是我的存储库模式的核心.
在EntityFrameworkRepository类的工作有两个泛型类型:
TDomainModel:丰富的域模型TEntityModel:实体框架数据实体定义了两种抽象方法:
ToDataEntity(TDomainModel):转换为数据实体(for Add()和Update()方法)ToDomainModel(TEntityModel):构建域模型(用于Find()方法). 这些方法的具体实现将定义所讨论的存储库所需的映射.
public interface IRepository<T> where T : DomainModel
{
T Find(int id);
void Add(T item);
void Update(T item);
}
public abstract class EntityFrameworkRepository<TDomainModel, TEntityModel> : IRepository<TDomainModel>
where TDomainModel : DomainModel
where TEntityModel : EntityModel
{
public EntityFrameworkRepository(IUnitOfWork unitOfWork)
{
// ...
}
public virtual TDomainModel Find(int id)
{
var entity = context.Set<TEntityModel>().Find(id);
return ToDomainModel(entity);
}
public virtual void …Run Code Online (Sandbox Code Playgroud) .net domain-driven-design entity-framework repository-pattern onion-architecture
我有一个具有编辑器和项目概念的域模型.
编辑拥有许多项目,项目不仅有编辑所有者,还有许多编辑成员.因此,编辑还有一些"加入"项目.
我正在采用DDD方法对此进行建模并使用Repository模式进行持久化.但是,我还没有足够好地确定模式,以确定我应该如何做到这一点.
我正在假设编辑器和项目可能在同一个聚合中,其中根是编辑器.因此,我可以获得一个编辑器,然后枚举其项目,并可以从那里枚举项目的成员编辑.
但是,如果我只被允许从我的存储库中检索编辑器,这是不是意味着当我获得拥有它们的编辑器时我必须从存储库加载所有项目?如果我想延迟加载成员编辑器,项目还需要对存储库的引用?
或者,如果我拆分聚合并拥有一个编辑器存储库和一个项目存储库,那么我应该如何处理两者之间的事务,例如将新项目添加到编辑器中?例如:
Editor e = new Editor("Editor Name");
editorRepository.Add(e);
Project p = e.CreateProject("Project Name");
projectRepository.Add(p); // These two lines
editorRepository.Save(e); // should be atomic
Run Code Online (Sandbox Code Playgroud)
我是否误解了Repository模式的意图?
domain-driven-design aggregate lazy-loading ddd-repositories repository-pattern
我很好奇,领域驱动设计和模型驱动架构之间有什么区别?我觉得他们有一些相似之处.
你能开导我吗?
谢谢
如何使用存储库模式以事务方式封装多个实体的保存?例如,如果我想根据订单创建添加订单并更新客户状态,但只有在订单成功完成时才会这样做?请记住,对于此示例,订单不是客户内的集合.他们是自己的实体.
这只是一个人为的例子,所以我并不关心订单是否应该在客户对象内部,甚至不在同一个有限的上下文中.我真的不在乎将使用什么底层技术(nHibernate,EF,ADO.Net,Linq等).我只是想看看一些调用代码在这个公认的全有或全无操作示例中的样子.
design-patterns domain-driven-design architectural-patterns repository-pattern
我正在创建我的第一个ASP.NET MVC站点,并一直在尝试遵循域驱动的开发.我的站点是一个项目协作站点,可以将用户分配到站点上的一个或多个项目.然后将任务添加到项目中,并将项目中的用户分配给任务.所以"用户"是我的域模型的基本概念.
我的计划是拥有一个"用户"模型对象,其中包含有关用户的所有信息,可以通过IUserRepository访问.UserId可以识别每个用户.虽然我现在还不确定我是否希望UserId是一个字符串或整数.
我的域对象User和IUserRepository应该如何与我的网站的更多管理功能相关,例如授权用户并允许他们登录?如何将我的域模型与ASP.NET的其他方面集成,例如HttpContext.User,HttpContext.Profile,自定义MemberShipProvider,自定义ProfileProvider或自定义AuthorizeAttribute?
我应该创建一个自定义MembershipProvider和/或包含我的IUserRepository的ProfileProvider吗?虽然,我也可以预见为什么我可能想要将我的域模型中的用户信息与我站点上的用户授权分开.例如,将来我可能希望从表单身份验证切换到Windows身份验证.
不尝试重新发明轮子并坚持使用ASP.NET内置的标准SqlMembershipProvider会更好吗?每个用户的个人资料信息都将存储在域模型(User/IUserRepository)中,但这不包括他们的密码.然后我会使用标准的ASP.NET成员资格来处理创建和授权用户?因此,在创建帐户或第一次登录时,需要在某个地方创建一些代码,以便为IUserRepository中的新用户创建配置文件.
c# ×2
.net ×1
aggregate ×1
architecture ×1
asp.net ×1
asp.net-mvc ×1
bdd ×1
cqrs ×1
domain-model ×1
java ×1
java-8 ×1
java-stream ×1
lazy-loading ×1
lookup ×1
modeling ×1
validation ×1