域驱动设计 – 聚合与数据模型
从存储整体请求聚合并作为单个单元处理.建议设计小型聚合不影响性能.这部分对我来说非常具有挑战性.特别是在持久化数据方面.
我有一个具有DueDate属性的Activity.活动的参与者可以参与活动的阶段,但仅限于DueDate之前. 如果已经存在对此阶段的贡献,我必须同时限制阶段内容更改. 除了来自不同参与者的贡献的并行交易之外不会相互影响. 这给了我一个提示,即ContributionToPhase必须是一个独立的聚合,并且可能通过标识引用Activity聚合. 数据模型如下: Activity ------------ Id Title Description DueDate .... Phase ------------ Id ActivityId Order Title Description .... ContributionToPhase ------------ Id PhaseId ParticipantId .... 可以看出,在数据模型中,Activity和ContributionToPhase之间没有直接的联系.如果我将其设计为事务脚本,我将创建一个特殊的DTO,其中包含验证特定事务所需的所有数据(但不是更多): ContributionRelatedDTO Id ActivtyId PhaseId UserId ActivityDueDate TimeStamp .... 要么 PhaseContentsRelatedDTO Id ActivtyId HasContributions Timestamp .... 那么我应该如何使用DDD范式来处理它呢? 解决方法
要解决DDD和ORM的这类问题,请尝试实施一些CQRS.我喜欢DDD,但我认为你不应该全心全意地遵循它,我认为DDD很好地让我们遵循良好的做法,但请记住它每天都由大师改进,因为它没有解决方案对于所有问题呢.
对于每个事务,我们称之为命令.要执行命令,我们需要CommandHandler和CommandData.我看到一个CommandData,因为它是一个DTO.在那里,你把执行所述命令所需的所有东西都放了. CommandHandler更像是一个小型服务,处理业务登录,因此它们属于Domain.让我们创建一个简单的例子: public interface ICommandHandler<T> { T Handle(T command); } public class ContributeToPhaseCommandData { public Guid ContributionToPhaseId { get; set; } public Guid ActivityId { get; set; } public Guid PhaseId { get; set; } public Participant Contributor { get; set; } public DateTime ActivityDueDate { get; set; } public bool Success { get; set; } public string CommandResultMessage { get; set; } public ContributeToPhaseCommandData( /* mandatory data in constructor */ ) { } } public class ContributeToPhaseCommandHandler : ICommandHandler<ContributeToPhaseCommandData> { public ContributeToPhaseCommandHandler( /* inject other services,if needed */ ) { } public ContributeToPhaseCommandData Handle(ContributeToPhaseCommandData command) { // do stuff here,you might set some response data in the 'command' and return it. // You might send a DomainEvent here,if needed. return command; } } 它们通常由应用层调用,以响应某些用例(有人为一个阶段做出贡献).在委托对域(命令处理程序)的调用之前,应用层应检查请求者(即用户或其他系统)是否具有执行此类操作的授权. 现在,我们如何获取数据来提供命令?如果您不需要,我认为您不应该强制加载完整聚合.只在需要时加载它. 有时候你有一个沉重的逻辑,需要完全的agreggates,因此你可以把它放在域模型/实体.虽然有时候你有更复杂的逻辑,你很难把它放在模型/实体中并且需要一些信息用于许多部分,而当你根本不需要所有东西时加载重聚合是不切实际的.这使你的模型有点贫血. 看起来您不需要完整聚合来为此方案应用域逻辑.我只是认为创建替代的较轻版本的聚合是没用的,除非有人证明相反(我可能是错的). 我试着尽可能地在应用程序层中尽可能地创建(KISS): public SomeResponseToCaller ContributeToPhase(ICommandHandler<ContributeToPhaseCommandData> command,Guid phaseId,IPrincipal caller,IAuthorizationService authorizer) { if (!authorizer.authorizes(caller)) this.ExceptionHandler.Handle("Caller is not authorized! Shall we log this info?"); using(var db = new ActivitiesContext()) { ContributeToPhaseCommandData data = db.Phases .Select(p => new ContributeToPhaseCommandData() { ActivityId = p.ActivityId,PhaseId = p.Id,Contributor = p.Activity.Participants.SingleOrDefault(part => part.Name == caller.Identity.Name) ActivityDueDate = p.Activity.DueDate }).SingleOrDefault(p => p.Id == phaseId); if (data == null) this.ExceptionHandler.Handle("Phase not found"); if (data.Contributor == null) this.ExceptionHandler.Handle("Caller is not a participant of this Activity!!!!"); data.ContributionToPhaseId = Guid.NewGuid(); var result = command.Handle(data); db.SaveChanges(); return new SomeResponseToCaller() { Success = result.Success,ContributionId = result.ContributionToPhaseId,Message = result.CommandResultMessage }; } } 这个ExceptionHandler是某种实现IExcepionHandler的类,它应该处理应用程序逻辑异常.它们可以在Application的类构造函数中注入.实际上,您甚至可以在构造函数中发送AuthorizationService,并将其重用于每个应用程序调用. 不仅仅在那里抛出异常的目的是使测试更容易. 现在让我们谈谈CQRS.简而言之,它的目的是将查询与存储分开.从Martin Fowler开始:
这种方法带来的一件好事是在执行命令之后,您可以将调用委托给具有非规范化数据的辅助存储,以用于只读目的.此辅助存储可能甚至不需要密钥和关系.这就像在某处存储您的DTO / ViewModel. 人们认为我们读取的数据超过了存储数据,因此这使您可以将数据存储在UI可以比以往更快地读取的状态中,从而“准备好呈现”数据.对于模型中的每个新更改,您可以插入除更新/删除之外的新注册表,因此可以更快速,更轻松地获取历史数据,差异和其他内容. 由您和您的企业决定存储多少,非规范化程度.由于现在存储越来越便宜,你可以考虑在二级存储中存储更多东西,因为它是相关的. 它也可能是另一种存储,如NoSQL,缓存(它会让我们缓存失效),由您决定.我并不是说实现这个很容易,我们应该定义这些层之间的事务级别,以及我现在不记得的其他东西. 因此我认为存储非规范化数据是好的,因为您将它们用于只读目的,并且要小心使它们与您的域模型存储(可能是带有EF的SQL)同步.我希望这有助于对此主题进行重新研究,我的示例的目标是根据具体情况建议替代解决方案,您应该尝试将良好的解决方案结合起来,在适合时使用CQRS,并在适合时使用聚合.允许将它们组合,直到有人证明相反(再次). (编辑:李大同) 【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容! |
- Omega:flexible,scalable schedulers for large compute cl
- implicit instantiation of undefined template 'std::
- XML学习笔记 5. XSLT
- 有没有办法在最新的Swift 3快照中使用C variadic函数?
- 如何将用户定义的宏传递给xcodebuild?
- zClip – ZeroClipboard.swf已删除 – 需要新文件
- ruby-on-rails – PDFKIT自定义字体
- 定制自己的flex淡入淡出效果组件
- 从XML文件生成Java类的在线资源
- ios – OBJ-C:核心绘图XY轴固定