Questions tagged «domain-driven-design»

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

3
DDD:将域事件处理程序放在哪里?
您能否告诉我您的观点,哪一层适合将域事件处理程序放置在DDD中?例如,我有应用程序服务来添加新合同,并且我想在合同添加后向联系人发送电子邮件通知,电子邮件发件人(处理ContractAdded事件)应用程序服务或域服务还是还有什么吗

2
在DDD中,域服务是否本质上仅仅是门面和/或中介模式?
在域驱动设计中,域层可以具有多个(传统)服务。例如,对于用户域,我们可能有: 一个UserFactory,它以不同的方式构建User对象 一个用户存储库,负责与基础结构层中的持久性服务进行交互 域层中的UserService是仅仅是这两个服务和基础结构层的中介者和/或Facade,还是它还有更多内容?

6
DDD符合OOP:如何实现面向对象的存储库?
DDD存储库的典型实现看起来不太像面向对象,例如一种save()方法: package com.example.domain; public class Product { /* public attributes for brevity */ public String name; public Double price; } public interface ProductRepo { void save(Product product); } 基础架构部分: package com.example.infrastructure; // imports... public class JdbcProductRepo implements ProductRepo { private JdbcTemplate = ... public void save(Product product) { JdbcTemplate.update("INSERT INTO …

2
如何真正找出面向对象设计中必须要做的事情?
首先免责声明:我真的不知道这个问题是否适合该网站,但是我仍然发现它不仅对我而且对其他初学者都是一个相关的问题。如果可以改善问题以使其适合此处,请指出int注释。如果不合适,也让我知道,如果可能的话,让我知道可以在哪里讨论,因为我没有找到任何好的论坛。 我在2009年学习PHP时就学会了编程。2012年晚些时候,我转到了C#和.NET。无论如何,编码不是问题,写下算法不是我的问题。我的实际问题是要知道必须编码什么才能达到要求,必须在哪里编码。 网络上提供的大多数课程都讨论了如何 -如何用某种语言编写代码,如何使用一些API集等。这不是我的意思。 这些年来,我读了很多有关一堆东西的信息:面向对象的分析和设计,设计模式,领域驱动的设计等等。例如,我了解SOLID原则,DDD的一些主要思想,例如聘请领域专家的必要性,开发无处不在的语言等。我敢说我的理论背景至少合理。 但是,当谈到练习时,我觉得自己是一场灾难。前一段时间,我需要继续开发已经由其他人开发的金融系统。这就是用C#和WinForms开发的那种“旧系统”。这是我第一次选择一个具有实际领域复杂性,业务规则等等的项目。 我承认,当我大多数时候收到要求时,我认为“这到底是怎么做的?” -我什至不知道如何开始着手研究需求以弄清楚必须做什么。我认为我的主要困惑是我必须编写什么代码,什么类,接口以及每条逻辑的去向,每件事必须在哪个类上。问题是我不知道从哪里开始。 大多数时候,经过很多思考,我最终想到了一些主意,但是我却永远不知道如何判断我的主意是否正确。 我的意思是我不认为这是一种理论上的不足,因为我说我已经阅读了有关软件体系结构和面向对象的一堆建议,但对确定实践中必须做的工作并没有多大帮助。 所以,我怎么能学会真正 做到面向对象的设计?我要学习的是:给定的需求知道如何开始在一个过程中进行工作,从而找出必须完成的工作以及每个代码所属的位置。我还如何学习判断我的想法是否正确? 我相信在这里不可能完全解释这一点作为答案。但是,我所寻找的可能是根据网站样式而定的答案,只是给出概述并指出一些参考资料(书籍,在线课程等),这些参考资料可用于扩展思想并真正学习这些知识。

2
持久性无关对象能够实现延迟加载吗?
持久性无知是单责任原则的应用,这实际上意味着域对象(DO)不应该包含与持久性相关的代码,而应该只包含域逻辑。 a)我认为这意味着联系较低层(即持久层)的代码位于业务逻辑层的其他类(OC)中的域模型之外? B)如果我的假设下一个)是正确的,那么DO,说Customer,从来没有包含的方法如GetCustomers或GetCustomerByID? c)如果我在a)和b)下的假设是正确的,并且假设Customer域对象对其属性的某些属性使用了延迟加载,则在某个时候Customer,内部逻辑必须联系OC,而OC则会检索延迟的数据。但是,如果Customer需要联系OC来接收延迟的数据,那么我们真的不能说域对象不包含与持久性相关的逻辑吗? 谢谢 回复jkohlhepp 1)我假设OrderProvider并且CustomerProvider类包含在业务逻辑层中? 2)我从您的答复中得知,b)下的假设是正确的? 3) ...我将检查是否填充了一些私人订单字段或该字段是否为空。如果为空... 但是据我所知,只要域代码需要检查是否order填充了私有字段,如果没有,请联系OrderProvider,我们已经违反了PI原则?

2
如何使这种设计更接近适当的DDD?
我已经读了几天DDD,并且需要有关此样本设计的帮助。当不允许域对象向应用程序层显示方法时,DDD的所有规则使我非常困惑应该如何构建任何内容。还有什么地方可以协调行为?不允许将存储库注入实体,因此实体本身必须在状态下工作。然后,一个实体需要从域中了解其他信息,但是其他实体对象也不被允许注入?这些事情中有些对我有意义,而另一些则没有。我还没有找到很好的例子来说明如何构建整个功能,因为每个例子都与订单和产品有关,一遍又一遍地重复其他例子。通过阅读示例,我学得最好,并且尝试使用到目前为止所获得的有关DDD的信息来构建功能。 我需要您的帮助来指出我做错了什么以及如何解决,最好是使用代码,因为在已经模糊定义所有内容的情况下,很难理解“我不会建议做X和Y”。如果我无法将一个实体注入另一个实体,则更容易了解如何正确地进行操作。 在我的示例中,有用户和主持人。主持人可以禁止用户,但有一条商业规则:每天仅3个。我尝试建立一个类图以显示关系(下面的代码): interface iUser { public function getUserId(); public function getUsername(); } class User implements iUser { protected $_id; protected $_username; public function __construct(UserId $user_id, Username $username) { $this->_id = $user_id; $this->_username = $username; } public function getUserId() { return $this->_id; } public function getUsername() { return $this->_username; } …

4
避免肿的域对象
我们正在尝试使用DDD方法将数据从庞大的Service层移至Domain层。当前,我们的服务中有很多业务逻辑,这些业务逻辑遍布各地,并且无法从继承中受益。 我们有一个中心的Domain类,这是我们大多数工作的重点-Trade。贸易对象将知道如何定价,如何估计风险,验证自身等。然后,我们可以用多态性替换条件。例如:SimpleTrade将以一种方式定价,而ComplexTrade将以另一种定价。 但是,我们担心这会使贸易类膨胀。它确实应该负责自己的处理,但是随着添加更多功能,类的大小将成倍增加。 所以我们有选择: 将处理逻辑放在贸易类中。现在,根据交易类型,处理逻辑是多态的,但是交易类别现在具有多种责任(定价,风险等)并且规模很大 将处理逻辑放入其他类,例如TradePricingService。Trade继承树不再具有多态性,而是类更小并且更易于测试。 建议的方法是什么?

5
如果“存储库模式”对于现代ORM(EF,nHibernate)过大,那么更好的抽象是什么?
我最近阅读了很多关于将存储库模式与功能强大的ORM(例如Entity Framework)一起使用的争论,因为它结合了类似存储库的功能以及工作单元功能。 另一个反对在单元测试之类的情况下使用该模式的论点是,存储库模式是一种泄漏抽象,因为更通用的实现利用了IQueryable。 反对使用存储库模式的论点对我来说很有意义,但是建议的替代抽象方法通常更令人困惑,并且看起来像问题一样矫kill过正。 吉米·鲍嘉(Jimmy Bogards)的解决方案似乎既要吹散抽象,又要介绍自己的体系结构。 https://lostechies.com/jimmybogard/2012/10/08/favor-query-objects-over-repositories/ 另一个不必要的存储库示例....但是使用我的体系结构! http://blog.gauffin.org/2012/10/22/griffin-decoupled-the-queries/ 另一个... http://www.thereformedprogrammer.net/is-the-repository-pattern-useful-with-entity-framework 我还没有找到一种明显的替代或替代方法,而该替代方法本身并不具有更多的体系结构,因此可以替代“过于复杂”的存储库模式。

2
CQRS +事件源:(是否正确)命令通常是点对点传递的,而域事件是通过pub / sub传递的?
我基本上是想围绕CQRS的概念和相关概念。 尽管CQRS不一定包含消息传递和事件源,但它似乎是一个很好的组合(从结合了这些概念的许多示例/博客中可以看出) 给定一个用于某种情况的状态更改的用例(例如,更新有关SO的问题),您是否认为以下流程是正确的(如最佳实践中那样)? 系统发出一个汇总的UpdateQuestionCommand,可以将其分成几个较小的命令:以Question Aggregate Root为目标的UpdateQuestion和以User Aggregate Root为目标的UpdateUserAction(对点进行计数)。这些是使用点对点消息传递异步发送的。 聚合根起作用,如果一切顺利,则分别引发事件QuestionUpdated和UserActionUpdated,它们包含外包给事件存储的状态。要保留yadayada,只是为了完整起见,此处并不是重点。 这些事件也被放在发布/订阅队列中进行广播。任何订阅者(其中可能有一个或多个创建阅读视图的投影仪)都可以自由订阅这些事件。 一个普遍的问题:最佳实践是命令之间进行点对点通信(即:接收方已知),而广播事件(即:接收方未知)吗? 假设以上所述,允许通过pub / sub而不是点对点广播命令的优点/缺点是什么? 例如:在使用Saga广播广播Commands时可能会出现问题,因为Saga在某个聚合根之一出现故障的情况下需要扮演的调解角色受到了阻碍,因为saga不知道哪个聚合根开始参与。 另一方面,我看到允许广播命令时的优势(灵活性)。

5
实体框架的域驱动设计的陷阱
我研究的许多DDD教程都涵盖了理论。它们都有基本的代码示例(Pluralsight和类似代码)。 在网络上,也有人尝试创建涵盖EF的DDD的教程。如果您只是简短地学习它们-您很快就会发现它们彼此之间有很大的不同。有些人建议保持应用程序最小化,并避免在EF之上引入其他层(例如,存储库),其他人则决定生成额外的层,甚至通过注入DbContext聚合根甚至违反SRP 。 如果要提出基于意见的问题,我深表歉意,但是... 在实践中,实体框架是功能最强大且使用最广泛的ORM之一。不幸的是,您不会找到涵盖DDD的综合课程。 重要方面: 实体框架可立即使用UoW和存储库(DbSet) 使用EF,您的模型具有导航属性 与EF所有的车型都始终可用的关闭DbContext(它们被表示为DbSet) 陷阱: 您不能保证子模型仅受“聚合根”影响-您的模型具有导航属性,可以修改它们并调用dbContext.SaveChanges() 与DbContext您可以访问每一个模型,从而规避聚合根 您可以通过将ModelBuilderin 标记为字段来通过in OnModelCreating方法来限制对根对象的子对象的访问-我仍然不认为这是进行DDD的正确方法,而且很难评估这种情况将来可能导致什么样的冒险(非常怀疑) 冲突: 如果没有实现返回聚合的另一层存储库,我们甚至无法部分解决上述陷阱 通过实现额外的存储库层,我们将忽略EF的内置功能(每个DbSet都已经是仓库)并且使应用程序过于复杂 我的结论是: 请原谅我的无知,但基于以上信息-要么是实体框架 不足以用于域驱动设计,或者域驱动设计是不完善且过时的方法。 我怀疑每种方法都有其优点,但是我现在已经完全迷失了,对如何将EF与DDD调和一无所知。 如果我错了-请问至少有人能详细介绍一下如何使用EF进行DDD的简单说明(甚至提供不错的代码示例)吗?

2
域/持久性模型隔离通常会很尴尬吗?
我深入研究了域驱动设计(DDD)的概念,发现一些原则很奇怪,尤其是在域和持久性模型的隔离方面。这是我的基本理解: 应用程序层(提供功能集)上的服务从其执行功能所需的存储库中请求域对象。 该存储库的具体实现从为其实现的存储中获取数据 该服务告诉封装业务逻辑的域对象执行某些修改其状态的任务。 该服务告诉存储库保留修改后的域对象。 存储库需要将域对象映射回存储中的相应表示形式。 现在,鉴于以上假设,以下内容似乎很尴尬: 广告2: 域模型似乎加载了整个域对象(包括所有字段和引用),即使请求它的功能不需要它们。如果引用了其他域对象,则甚至可能根本无法完全加载,除非您同时加载这些域对象以及它们依次引用的所有对象,依此类推。想到了延迟加载,但是这意味着您开始查询域对象,这首先应该是存储库的责任。 鉴于此问题,加载域对象的“正确”方式似乎具有针对每种用例的专用加载功能。这些专用功能将仅加载其设计用例所需的数据。尴尬在这里发挥了作用:首先,我必须为存储库的每个实现维护大量的加载函数,并且域对象最终将null以其字段中携带的不完整状态结束。从技术上讲,后者应该不是问题,因为如果未加载值,则无论如何要求它的功能都不应要求该值。仍然很尴尬和潜在的危害。 广告3 .: 如果没有存储库的任何概念,领域对象将如何验证唯一性对构造的约束?例如,如果我想创建一个User具有唯一社会保险号(已给出)的新保险,则只有在数据库中定义了唯一性约束的情况下,最早的冲突将在要求存储库保存对象时发生。否则,User在创建新的社会保障之前,我可以寻找具有给定社会保障的a并报告错误(如果存在)。但是,约束检查将存在于服务中,而不存在于它们所属的域对象中。我刚刚意识到,域对象被很好地允许使用(注入)存储库进行验证。 广告5: 与让域对象直接修改底层数据相比,我认为将域对象映射到存储后端是一项工作量很大的过程。当然,将具体的存储实现与域代码脱钩是必不可少的前提。但是,确实付出了这么高的代价吗? 您显然可以选择使用ORM工具为您执行映射。这些通常需要您根据ORM的限制来设计域模型,或者甚至引入从域到基础结构层的依赖关系(例如,通过在域对象中使用ORM批注)。我也读过ORM引入了大量的计算开销。 对于NoSQL数据库(几乎不存在任何类似于ORM的概念),您如何跟踪域模型中的哪些属性发生了变化save()? 编辑:另外,为了使存储库访问域对象的状态(即每个字段的值),域对象需要显示其内部状态,这会破坏封装。 一般来说: 交易逻辑将流向何方?这当然是持久性特定的。一些存储基础架构甚至可能根本不支持事务(例如内存中的模拟存储库)。 对于修改多个对象的批量操作,我是否必须分别加载,修改和存储每个对象才能通过对象的封装验证逻辑?这与直接对数据库执行单个查询相反。 我希望对此主题进行一些澄清。我的假设正确吗?如果没有,解决这些问题的正确方法是什么?

3
在DDD中,存储库应公开实体还是域对象?
据我了解,在DDD中,将存储库模式与聚合根一起使用是合适的。我的问题是,是否应将数据作为实体或域对象/ DTO返回? 也许一些代码可以进一步解释我的问题: 实体 public class Customer { public Guid Id { get; set; } public string FirstName { get; set; } public string LastName { get; set; } } 我应该做这样的事情吗? public Customer GetCustomerByName(string name) { /*some code*/ } 还是类似的东西? public class CustomerDTO { public Guid Id { get; set; …

1
如何在CQRS + Event Sourcing架构中处理Add / Create *命令
我想使用CQRS模式和Event Sourcing来实现我的第一个应用程序。我想知道如何正确处理聚合根的创建。假设有人发送CreateItem命令。应该如何处理?事件ItemCreated应该存储在哪里?作为新项目的第一事件?还是我应该具有某种ItemList实体来聚合所有项目,并且其事件列表仅由ItemCreated事件组成? 乌迪·达汉( Udi Dahan)建议不要创建聚合根,而应始终使用某种获取方法。但是我如何获取新的东西,当然还没有分配任何ID。我理解背后的想法,并且认为一个新对象是其状态由零个事件组成的对象是很合理的。但是我应该如何使用呢?我的存储库中应该有一个与众不同的方法吗?getNewItem()还是让我的get(id)方法接受Optional<ItemId>? 编辑:经过一段时间的挖掘,我发现使用actor对上述模式进行了非常有趣的实现。作者不是创建聚合,而是从某种具有新创建的UUID的存储库中检索聚合。这种方法的缺点是他允许出现临时的不一致状态。我也想知道如何delete使用这种方法来实现方法。只需将Deleted事件添加到聚合的事件列表中?

1
架构模块化服务应用程序
我正在考虑设计一种本质上是模块化的新解决方案,并希望创建一个支持该设计的结构,以便将来轻松进行扩展,明确分离关注点,按模块进行许可等。在网络上发现的关于模块化或组合应用程序的是以UI为中心的,专注于Silverlight,WPF等。就我而言,我正在开发WCF服务应用程序,该程序将由从事各种UI项目的其他开发人员使用。 背景 我的项目的目标是创建一个集中的业务/域逻辑源,以支持我们的多个当前重复流程,规则等的业务应用程序。虽然并非可以实现模块化的所有好处,但我想抓住建立一个可以利用我们可以利用的那些部分的框架的机会。 我通过查看服务应用程序公开的API来开始设计。显然,可以按照我所考虑的模块化路线来隔离服务。例如,我将拥有FinanceService,InventoryService,PersonnelService等服务,这些服务将我的服务操作分组以在API中提供高内聚性,同时保持较低的耦合度,因此客户端仅需使用与他们的应用程序相关的服务。 对于我来说,我可以为每个服务分别拥有单独的模块,例如MyApp.Finance,MyApp.Inventory,My.Personnel等。跨领域关注点和共享类型将在MyApp共享程序集中。从这里我有点束缚。 (哦,我应该提一下,我将使用IoC容器进行依赖注入来保持应用程序的松散耦合。我不会提及哪个容器,因为我不想打开Pandora的盒子!) 在MyApp.ServiceHost中,我将创建一个与每个模块相对应的服务宿主文件(.svc),例如FinanceService.svc。服务主机需要服务的名称,该名称与配置文件中的信息相对应,该配置文件包含定义服务合同的接口。然后,将IoC配置用于映射要使用的接口的具体实现。 1.服务层应该实现API并委托给模块,还是应该使模块自包含(因为它们包含与该模块相关的所有内容,包括服务实现)? 解决该问题的一种方法是拥有一个MyApp.Services“模块”,其中包含服务合同的实现。每个服务类都简单地委派给包含该操作的域逻辑的相应模块中的另一个类。例如,MyApp.Services中的FinanceService WCF类委托给另一个接口,该接口在Finance模块中实现以执行操作。例如,这将使我能够维护精简的服务外观并将实现“插入”到服务实现中,并且无需使模块担心WCF。 另一方面,也许每个模块都是独立的,因为它具有接口和实现。服务主机引用模块中的服务合同接口,并且IoC也配置为使用模块中的适当实现。这意味着除了添加新的.svc文件和IoC配置信息外,无需更改服务层即可添加新模块。 我正在考虑如果从标准WCF切换到RESTful服务接口,或者转而使用RIA服务之类的影响。如果每个模块都包含服务合同的实现,那么如果我更改服务技术或方法,则必须在每个模块中进行更改。但是,如果外观是其自己的模块,那么我只需要换出该部分即可进行更改。然后,这些模块将必须实现一组可能在共享程序集中定义的合同(接口)的不同集合??? 2.处理模块之间的资源共享和/或模块之间的依赖关系的最佳方法是什么? 以接收操作为例。乍一看,这很有意义,因为进货是库存功能,因此进入库存模块。但是,在财务方面,我们还需要生成收据并授权付款。 一方面,我希望使用某种类型的域事件/消息传递来传达操作。库存模块引发一个GoodsReceivedEvent,该事件由Financial模块处理以生成收据并启动付款过程。但是,这意味着财务模块需要了解已收到的库存项目。我可以简单地通过ID来引用它们,但是如果我需要收据的其他信息,例如名称和/或描述,单位成本等,该怎么办?每个模块都拥有自己的版本的清单项目以适应该模块的需求是否有意义?在这种情况下,财务模块在处理GoodsReceivedEvent时将必须执行自己的库存项目查找。 ... 我之所以使用此示例,是因为我已经与各种ERP系统进行了大量合作,并且知道它们是采用这种类型的模块化设计的-我只是不知道如何操作。我上面也没有明确提及,但是我更喜欢遵循域驱动设计原则来设计解决方案,并相信这种类型的模块化恰好适合该领域。 非常感谢我对此有所帮助。

2
DDD中的例外
我正在学习DDD,并且正在考虑在某些情况下引发异常。我知道对象不能进入错误状态,因此这里的异常很好,但是在许多示例中,例如,如果我们尝试使用数据库中存在的电子邮件添加新用户,也会抛出异常。 public function doIt(UserData $userData) { $user = $this->userRepository->byEmail($userData->email()); if ($user) { throw new UserAlreadyExistsException(); } $this->userRepository->add( new User( $userData->email(), $userData->password() ) ); } 因此,如果存在使用此电子邮件的用户,那么我们可以在应用程序服务中捕获异常,但我们不应该使用try-catch块来控制应用程序的操作。 最好的方法是什么?

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.