Questions tagged «domain-driven-design»

域驱动设计(DDD)是一种通过将实现与不断发展的模型连接来开发满足复杂需求的软件的方法。

2
DDD:一个根聚合保留对另一个根聚合的引用是否正确?
遵循域驱动设计(DDD)时,对于根聚合保留对恰好是单独聚合中根实体的内部实体的引用是否正确? 我认为这是不正确的,主要是因为这本关于蓝皮书的规则: 除了根ENTITY,AGGREGATE边界之外的任何内容都不能保存对内部任何内容的引用。根ENTITY可以将对内部ENTITIES的引用传递给其他对象,但是这些对象只能临时使用它们,并且它们可能不会保留引用。根可以将VALUE OBJECT的副本交给另一个对象,它发生什么都没有关系,因为它只是一个VALUE,不再与AGGREGATE有任何关联。 如果一个根聚合包含对另一个根聚合的引用,则违反了前者的边界,并且聚合的整个概念都已损坏,因此我认为,如果一个根聚合看起来像需要保留对另一个根聚合的引用,那么我需要创建一个不同的实体,该实体可能会与另一个根实体共享一些相同的成员,但不会具有全局标识,因为本书中的另一条规则指出: 根实体具有全球性。边界内的实体具有本地身份,仅在AGGREGATE内部具有唯一性。 我相信这将是正确的方法,但是由于它具有重复性和冗余性(当从纯DOP脱离DDD的上下文时),我要求提供一些反馈。

2
实施DDD:用户和权限
我正在研究一个小型应用程序,试图掌握域驱动设计的原理。如果成功,这可能是一个更大项目的试验。我正在尝试遵循《实现域驱动的设计》(由Vaughn Vernon撰写)一书,并试图实现一个类似的简单讨论论坛。我还在github上签出了IDDD示例。我在采用身份和案件访问权方面遇到一些困难。让我给出一些背景信息: 我(希望)理解了将用户和权限逻辑分开的原因:这是一个支持域,并且是一个不同的有界上下文。 在核心域中,没有用户,只有作者,主持人等。这些是通过使用服务延伸到“身份和访问”上下文,然后将接收到的User对象转换为and主持人而创建的。 以相关角色作为参数调用域操作:例如: ModeratePost( ..., moderator); 域对象的方法检查给定的主持人实例是否不为空(如果从“身份和访问”上下文中询问的用户不具有主持人角色,则主持人实例将为空)。 在一种情况下,它会在更改帖子之前进行另一项检查: if (forum.IsModeratedby(moderator)) 我的问题是: 在后一种情况下,安全性关注点是否不会再次混入核心域?以前,这些书指出“可以与谁一起发布主题,或者在允许的条件下发布。论坛只需要知道作者正在这样做”。 本书中基于角色的实现非常简单:当主持人是核心域时,它会尝试将当前的userId转换为主持人实例,或在需要时将其转换为作者。服务将以适当的实例进行响应;如果用户没有所需的角色,则该服务将为null。但是,我看不到如何适应更复杂的安全模型。我正在尝试的当前项目具有一个包含组,ACL等的相当复杂的模型。 即使规则不是很复杂,例如:“帖子只能由其所有者或编辑者编辑”,但这种方法似乎无法使用,或者至少我看不到实现该方法的正确方法。 通过向Identity and Access上下文询问OwnerOrEditor实例并不适合,我最终会在核心域中得到越来越多与安全性相关的类。另外,我不仅需要将userId传递给安全上下文,还需要将受保护资源的标识符(帖子,论坛等的ID)传递给安全性上下文,该上下文可能不关心这些事情(对吗? ) 通过将权限拉到核心域并在域对象的方法或服务中检查它们,我将得出结论:将安全问题与域混合在一起。 我在某个地方读过(并且我倾向于同意),这些与权限相关的事物不应成为核心域的一部分,除非安全性和权限是核心域本身。像上面给出的那样简单的规则是否足以使安全性成为核心域的一部分?

6
自治微服务,事件队列和服务发现
最近,我一直在阅读有关微服务的很多文章,这是到目前为止我得出的一些结论(如果我在任何时候错了,请更正我)。 微服务架构与域驱动设计配合得很好。通常一个MS代表一个有界上下文。 如果微服务A需要驻留在微服务B中的功能,则我的模型可能是错误的,并且A和B 实际上应该是一个微服务/ BC。 微服务之间的同步通信(直接HTTP请求)很糟糕,导致它违背了微服务的目的,并引入了组件之间的耦合。 服务之间的异步通信是可取的。服务应将事件发布到消息队列,以便其他服务可以订阅和处理事件的一部分,或使用它复制上下文所需的部分数据。这样,服务可以处理请求,甚至其他服务也已关闭,而在同步通信中则不会。 如果微服务A发布事件,微服务B订阅该事件并产生一个新事件作为结果,则微服务A不应是一个正在处理的新创建事件,因为这将是循环依赖性。在这种情况下,我们应该引入第三种微服务,或者将A和B合并到AB微服务中。 微服务实际上是一个误导性术语。我们应该为小环境而努力,但这不是必须的。术语不应该是“微服务”,而是“ 足够大以进行工作服务 ”。 微服务使我们可以更轻松地引入新功能,而不必担心会破坏整个系统。可以通过引入新服务或重构现有服务之一来完成。 每个微服务都应具有自己的数据存储。数据复制/复制是此体系结构中的理想行为。 除了证实我对这种体系结构的理解之外,问题的其他部分主要与服务发现有关。如果服务异步通信,并使用亚马逊SQS之类的中央事件队列,这是否意味着服务发现在这样的体系结构中没有位置? 服务不应对系统中的其他服务有任何了解。他们只知道应该发布或订阅的上下文和事件吗?

2
对于域驱动的设计RESTful Web服务,这是否是一个好的Visual Studio解决方案结构?
我正在构建.NET 4.5 C#Web API RESTful解决方案,我想告诉我我的项目解决方案对于使用域驱动设计设计的解决方案是否正确和/或明智(足够?)。 该解决方案已分为6个项目: /基础 (没有任何引用) 该Web项目构成了解决方案与外界之间的接口。包含Web API控制器。除了从请求对象中收集值并要求BizApi层工作以外,几乎没有任何逻辑。 /Biz.Api (由基础引用) 提供域服务,并允许/ Base接口项目访问/Biz.Domain项目中的域业务逻辑对象。 /Biz.Domain (由Biz.Api引用) 提供Biz.Api层的域类。这些提供了操作内存中业务数据的方法。 /Dal.Db (由Biz.Api引用) 数据库存储库层。访问数据库并将返回的数据映射到/ Interfaces层中定义的内部DTO。 /Dal.Services (由Biz.Api引用) 为外部依赖项(如Web服务)提供代理层,并将其返回的数据映射到/ Interfaces项目中定义的内部DTO。 /接口 (以上大多数项目引用) 包含用于在解决方案中传递数据的DTO类和用于为IoC之类的合同定义合同的C#接口。

3
聚合根何时应包含另一个AR(何时不包含)
首先让我为这篇文章的长度道歉,但是我真的很想在前面传达尽可能多的细节,所以我不会浪费您的时间在评论中来回走动。 我正在按照DDD方法设计应用程序,并且想知道我可以遵循什么指南来确定聚合根是否应该包含另一个AR,或者是否应该将它们保留为单独的“独立” AR。 以一个简单的“时钟”应用程序为例,该应用程序允许员工在一天之内或每天进行自我计时。用户界面允许他们输入员工ID和PIN,然后对其进行验证并检索员工的当前状态。如果该员工当前正在上班,则UI会显示一个“ Clock Out”按钮。相反,如果它们没有被计时,则按钮显示为“ Clock In”。该按钮采取的操作也对应于员工的状态。 该应用程序是一个Web客户端,它调用通过RESTful服务接口公开的后端服务器。我在创建直观,易读的URL时的第一步是产生了以下两个端点: http://myhost/employees/{id}/clockin http://myhost/employees/{id}/clockout 注意:在验证员工ID和PIN并在标头中传递表示“用户”的“令牌”后,才使用它们。这是因为存在一种“经理模式”,允许经理或主管调入或调出另一名员工。但是,为了便于讨论,我试图使其保持简单。 在服务器上,我有一个提供API的ApplicationService。我对ClockIn方法的最初想法是: public void ClockIn(String id) { var employee = EmployeeRepository.FindById(id); if (employee == null) throw SomeException(); employee.ClockIn(); EmployeeRepository.Save(); } 这看起来很简单,直到我们意识到员工的时间卡信息实际上是作为交易列表维护的。这意味着每次调用ClockIn或ClockOut时,我不会直接更改Employee的状态,而是将新条目添加到Employee的TimeSheet中。Employee的当前状态(已计时或未计时)是从TimeSheet中的最新条目得出的。 因此,如果我使用上面显示的代码,则我的存储库必须认识到Employee的可持久属性没有更改,但是在Employee的TimeSheet中添加了新条目并执行到数据存储的插入。 另一方面(这是帖子的最终问题),TimeSheet似乎既是聚合根又具有身份(员工ID和期间),我可以轻松地实现与TimeSheet.ClockIn相同的逻辑(员工ID)。 我发现自己正在辩论这两种方法的优点,并且如开篇所述,想知道我应该评估哪种标准以确定哪种方法更适合该问题。

5
如何结合严格的TDD和DDD?
TDD是关于在测试的指导下设计代码的。 因此,通常不预先构建典型的层。它们应该在重构步骤中稍微出现。 域驱动的设计涉及许多技术模式,它们定义了完善的层,如应用程序层,基础结构层,域层,持久性层。 要从头开始DDD项目的编码部分,应如何操作? 我是否应该严格让设计从测试中脱颖而出,这意味着没有关注点分离(没有层次)和重构以适合DDD技术模式? 还是应该创建那些空层(应用程序,实体/域服务,基础结构),并让TDD独立地适合每个层(使用模拟在层之间进行隔离)?

2
DDD CQRS-每个查询和每个命令的授权
摘要 是否应按命令/查询实现CQRS / DDD中的授权? 我是第一次使用或多或少严格使用DDD CQRS模式的在线应用程序。我遇到了一个问题,我无法真正解决这个问题。 我正在构建的应用程序是分类帐应用程序,允许人们创建分类帐,也允许其他人查看/编辑/删除分类帐,例如员工。分类帐的创建者应该能够编辑他创建的分类帐的访问权限。甚至可以更改所有权。该域具有两个聚合TLedger和TUser。 我读了很多有关DDD / CQRS关键字的文章,涉及安全性,授权等。大多数人说授权是通用子域,除非正在构建安全应用程序。 在这种情况下,核心域当然是对交易,余额和帐户感兴趣的会计域。但是还需要能够管理对分类帐的细粒度访问的功能。我想知道如何用DDD / CQRS术语进行设计。 在DDD教程中,所有命令都是通用语言的一部分。它们很有意义。它们是代表“真实事物”的具体动作。 因为所有这些命令和查询都是用户将在“现实生活”中执行的实际操作,所以授权的实现是否应与所有这些“命令”和“查询”结合使用?例如,用户将具有执行TLedger.addTransaction()的权限,但没有执行TLedger.removeTransaction()的权限。或者,将允许用户执行查询“ getSummaries()”,但不执行“ getTransactions()”。 三维映射将以用户分类帐命令或用户分类帐查询的形式存在,以确定访问权限。 或者,以分离的方式,将为用户注册名为“权限”的权限。然后将为特定命令映射权限。例如,权限“ ManageTransactions”将允许用户执行“ AddTransaction()”,“ RemoveTransaction()”等。 权限映射用户->分类帐->命令/查询 权限映射用户->分类帐->权限->命令/查询 那是问题的第一部分。或简而言之,CQRS / DDD中的授权应按命令还是按查询实施?还是应该将授权与命令分离? 其次,关于基于权限的授权。用户应该能够管理其分类帐或允许其管理的分类帐上的权限。 授权管理命令在分类帐中发生 我想到了将事件/命令/处理程序添加到Ledger聚合中,例如GrantPermission(),revokePermission()等。在这种情况下,强制执行这些规则将发生在命令处理程序中。但这将要求所有命令都包含发出该命令的用户的ID。然后,我将检查到TLedger中是否存在该用户执行该命令的权限。 例如 : class TLedger{ function addTransactionCmdHandler(cmd){ if (!this.permissions.exist(user, 'addTransaction') throw new Error('Not Authorized'); } } 用户中的授权管理命令 另一种方法是将权限包括到TUser中。TUser将具有一组权限。然后,在TLedger命令处理程序中,我将检索用户并检查他是否有权执行该命令。但这将要求我为每个TLedger命令获取TUser聚合。 class TAddTransactionCmdHandler(cmd) { …

5
DDD,佐贺(Saga)和事件来源:可以将补偿行为简单地删除事件存储区吗?
我意识到上述问题可能会引起一些“什么?”,但让我尝试解释一下: 我试图将一些相关概念,基本上是Saga模式(http://www.rgoarchitects.com/Files/SOAPatterns/Saga.pdf)与事件源(DDD概念)结合起来:http : //en.wikipedia.org/wiki/Domain-driven_design) 一个很好的文章,将其包装在一起: https //blog.jonathanoliver.com/cqrs-sagas-with-event-sourcing-part-ii-of-ii/ 我一分钟就会解决这个问题,但是我想我必须先总结一下我对它的理解(这很可能是错误的,所以请纠正这种情况),因为这很可能会影响我为什么提出以下问题: Saga模式是一种代理,它给定操作(最终用户,自动化等,本质上是要更改数据的任何操作),将该操作划分为业务活动,并将这些活动中的每一个作为消息发送给Message Bus,依次将其发送到各个聚合根进行处理。 这些聚集的根可以完全自主地运行(关注点分离得很好,可扩展性强等)。 Saga实例本身不包含任何业务逻辑,该逻辑包含在向其发送活动的聚合根中。Saga中包含的唯一“逻辑”是“过程”逻辑(通常实现为状态机),它根据收到的动作(以及后续事件)确定要做什么(即:发送什么活动) Saga模式实现了一种分布式事务模式。即:当聚合根之一(可以再次自动工作,而又不了解彼此的存在)失败时,可能必须回滚整个操作。 这是通过让所有聚合根实现的,将它们的活动报告完成后返回给Saga。(无论成功还是失败) 如果所有聚合根均返回成功,则内部状态机是否由Saga决定下一步(或决定已完成) 在失败的情况下,Saga将参与最后一个动作的所有聚合根发出一个所谓的补偿动作,即:撤销每个聚合根所做的最后一个动作的动作。 如果操作是“加1票”,则可能只是在进行“减1票”,但可能会更复杂,例如将博客文章还原到以前的版本。 事件源(请参阅结合了这两个博客文章)旨在将保存在外部的每个汇总根源的每个活动的结果保存到集中式事件存储中(在这种情况下,这些更改称为“事件”) 此事件存储区是“事实的单一版本”,可用于简单地通过迭代存储的事件来重播所有实体的状态(本质上类似于事件日志) 将两者结合起来(即:让聚集的根使用事件源来将其更改外包,然后再将其报告回Saga)可以实现很多不错的可能性,其中之一是我的问题... 我觉得我需要把这件事从肩膀上移开,因为一口气要掌握很多。鉴于此上下文/心态(再次,请纠正错误) 问题:当汇总根接收到补偿动作并且如果该汇总根已使用事件源外包将其状态更改外包时,补偿动作不是在所有情况下都只是删除事件存储中针对该事件的最后一个事件给定总根?(假设持久实现允许删除) 这对我来说很有意义(这将是这种组合的另一个很大的好处),但是正如我所说的那样,我可能基于对这些概念的错误/不完全理解而做出这些假设。 我希望这不会太冗长。 谢谢。

7
原始vs类代表简单的域对象?
什么时候使用特定于域的对象与普通字符串或数字的通用准则或经验法则是什么? 例子: 年龄层vs整数? FirstName类与String? 唯一ID与字符串 PhoneNumber类vs字符串vs长? 域名类与字符串? 我认为大多数OOP从业人员肯定会说PhoneNumber和DomainName的特定类。有关使它们变得有效以及如何进行比较的更多规则,使简单的类更易于处理和更安全。但是对于前三个,还有更多的争论。 我从来没有遇到过“年龄”类,但是有人认为它必须是非负数是有道理的(好吧,我知道您可以为负数年龄辩护,但这是一个很好的例子,它几乎等同于原始整数)。 字符串通常代表“名字”,但并不是完美的,因为空字符串是有效字符串,但不是有效名称。比较通常会忽略大小写。当然,有一些方法可以检查是否为空,不区分大小写,等等,但这需要使用者执行此操作。 答案是否取决于环境?我主要关心的是企业/高价值软件,该软件可以生存和维护超过十年。 也许我想得太多了,但是我真的很想知道是否有人对何时选择类还是原始有规则。

2
如何在事件源中实施流程管理器
我正在研究一个小示例应用程序,以学习CQRS和事件源的概念。我有一个Basket汇总和一个Product应该独立工作的汇总。 这是一些伪代码来展示实现 Basket { BasketId; OrderLines; Address; } // basket events BasketCreated { BasketId; } ItemAdded { BasketId; ProductId; Quantity } AddItemSucceeded { BasketId; ProductId; Quantity } AddItemRevoked { BasketId; ProductId; Quantity } ItemRemoved { BasketId; ProductId; Quantity } CheckedOut { BasketId; Address } Product { ProductId; Name; Price; } …

1
如何记录无所不在的语言?
我们公司正在将大量手动业务流程(以及相关的机构知识)转换为新的企业软件。该项目的进展非常顺利,但是随着我们的进行,很明显,业务和开发方面的术语和定义存在很多混乱。 我已经知道Evan关于形成一种普遍存在的语言的争论已有一段时间了,但这是我第一次需要正式记录它们。当我环顾四周并尝试思考在哪里/如何记录我们的UL条款时,我有点迷失了。 其他公司如何记录无所不在的语言?这只是一个Wiki风格的词典吗?是否有用于此目的的工具?

4
从域访问存储库
假设我们有一个任务记录系统,当记录一个任务时,用户指定一个类别,并且该任务默认为“未完成”状态。在这种情况下,假定类别和状态必须作为实体实施。通常我会这样做: 应用层: public class TaskService { //... public void Add(Guid categoryId, string description) { var category = _categoryRepository.GetById(categoryId); var status = _statusRepository.GetById(Constants.Status.OutstandingId); var task = Task.Create(category, status, description); _taskRepository.Save(task); } } 实体: public class Task { //... public static void Create(Category category, Status status, string description) { return new Task …

3
分层架构中的验证和授权
我知道您在思考(或大喊大叫),“不是另一个问题,它要求验证在分层体系结构中属于什么?!?” 好吧,是的,但是希望这会在这个问题上有所不同。 我坚信验证的形式多种多样,是基于上下文的,并且在体系结构的每个级别都有所不同。这是后期的基础-有助于确定应在每个层中执行哪种类型的验证。另外,经常出现的一个问题是授权检查在哪里。 示例场景来自餐饮业务的应用程序。白天,司机可能会不定期地将卡车上载到现场的任何多余现金上交办公室。该应用程序允许用户通过收集驾驶员的ID和金额来记录“现金下降”。这是一些基本代码来说明涉及的层: public class CashDropApi // This is in the Service Facade Layer { [WebInvoke(Method = "POST")] public void AddCashDrop(NewCashDropContract contract) { // 1 Service.AddCashDrop(contract.Amount, contract.DriverId); } } public class CashDropService // This is the Application Service in the Domain Layer { public void AddCashDrop(Decimal amount, Int32 driverId) …

3
域驱动设计中的域对象是否应该仅是写操作?
我已经阅读了近两年的关于域驱动设计的文章,并且一直在谨慎地将一些概念引入我的日常工作中,或者至少计划了如何在域驱动设计中定期完成工作的计划。 我开始得出一个结论,尤其是在阅读了有关事件源和命令查询责任隔离(CQRS)的更多信息后,域对象可能仅用于写目的。更清楚地说,在我阅读的许多文档中,人们似乎巧妙地暗示了域对象负责进行以域为中心的操作/计算,验证,然后在那里主要是为持久化提供了一条途径存储库实现中提供的基础结构。尽管我喜欢这样的事实,因为它可以大大简化领域模型,因为它消除了公开状态的责任。 如果确实确实将域对象主要用作只写对象,那么这对我提出了一些问题,希望有人可以回答。 如何对具有setter的对象或修改对象状态但不提供向外公共接口以从诸如C#中的属性获取器读取状态的方法执行单元测试?仅仅为了使该对象可测试就可以公开状态吗? 如何向用户显示在域中完成的计算或运算的结果,而不必持久存储它们,然后将结果从持久存储中拉出到域的上下文之外?仅出于显示结果的目的而公开状态是否可以? 根据经验,唯一的属性获取器(获取访问器)应该是在域中也可写的属性吗?还是换句话说,只读属性应该唯一避免,因为它们仅出于读取目的存在,因此在实际的域模型中不发挥必要的作用? 相关资料: TDD,DDD和封装

5
域实体是否违反单一责任原则?
实体的单一责任(改变的原因)应该是唯一地标识自己,换句话说,是可以发现其责任的。 埃里克·埃文(Eric Evan)的DDD书,第16页。93: 实体的最基本责任是建立连续性,使行为清晰可预测。如果他们备用,他们会做到最好。与其着重于属性甚至行为,不如将实体对象的定义简化为最固有的特征,尤其是那些识别该特征或通常用于查找或匹配该特征的特征。仅添加对于该行为所需的概念和属性必不可少的行为。 除此之外,还希望将行为和属性删除到与核心Entity相关联的其他对象中。除了身份问题外,实体还倾向于通过协调其拥有的对象的操作来履行其职责。 1。 ...将ENTITY对象的定义简化为最固有的特征,尤其是那些识别它或通常用于查找或匹配它的特征。仅添加对该概念必不可少的行为... 为实体分配一个唯一的ID后,它的身份就会建立,因此我认为这样的实体不需要任何行为就能维持其身份或帮助其识别自己。因此,我不明白作者用“ 对于概念必不可少的行为 ” 指的是什么行为(除了find和match 操作之外)? 2。 ...将ENTITY对象的定义简化为最固有的特征,尤其是那些识别它或通常用于查找或匹配它的特征。...除此之外,还希望将行为和属性删除到与核心ENTITY相关的其他对象中。 因此,任何无助于识别实体的行为,但我们仍将其表征为该实体的固有特征(即吠叫是狗固有的,飞行是飞机固有的,产卵是鸟类固有的.. ),应该放入与该实体关联的其他对象中(例如:我们应该将吠叫行为放入与dog实体关联的对象中)? 3。 除此之外,还希望将行为和属性删除到与核心ENTITY相关联的其他对象中。 一)MyEntity代表的职责A_resp和B_resp对象a,并b分别。 即使大多数的A_resp和B_resp工作是由做a和b的情况下,客户端仍担任A_resp并B_resp通过MyEntity,这意味着,从客户的角度来看,这两个职责属于MyEntity。这样,难道这不是意味着MyEntity也有A_resp和B_resp责任,因此违反了SRP? b)即使我们假设A_resp并且B_resp不属于MyEntity,MyEntity仍然有责任AB_resp协调对象a和的操作b。因此,不MyEntity违反SRP,因为它至少具有两项职责 –唯一标识自己,以及AB_resp? class MyEntity { private A a = ... private B b = ... public A GetA() { ... } public B GetB() { ... } /* coordinates operations …

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.