Questions tagged «domain-driven-design»

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

8
什么时候原始的迷恋不是代码的味道?
最近,我读了很多文章,将原始的痴迷描述为一种代码气味。 避免原始痴迷有两个好处: 它使域模型更加明确。例如,我可以与业务分析师讨论邮政编码,而不是包含邮政编码的字符串。 所有验证都放在一个地方,而不是整个应用程序。 那里有很多文章描述什么是代码气味。例如,我可以看到为这样的邮政编码除去原始的困扰的好处: public class Address { public ZipCode ZipCode { get; set; } } 这是ZipCode的构造函数: public ZipCode(string value) { // Perform regex matching to verify XXXXX or XXXXX-XXXX format _value = value; } 您将打破DRY原则,将验证逻辑放在所有使用邮政编码的地方。 但是,以下对象呢? 出生日期:检查是否大于预期且小于今天。 薪金:检查是否大于或等于零。 您将创建DateOfBirth对象和Salary对象吗?好处是您可以在描述域模型时谈论它们。但是,这是过度工程的一种情况,因为没有太多的验证。是否有一条规则描述了何时以及何时不消除原始的困扰,或者如果可能的话,您应该始终这样做吗? 我想我可以创建一个类型别名而不是一个类,这将有助于上面的第一点。

2
域驱动设计-实体问题中的外部依赖关系
我想启动域驱动设计,但是在开始之前我想解决一些问题:) 假设我有一个网上论坛和用户,当用户想要加入网上论坛时,我正在调用groupsService.AddUserToGroup(group, user)method。在DDD中,我应该这样做group.JoinUser(user),看起来不错。 如果我有一些添加用户的验证规则,或者将用户添加到组中时需要启动一些外部任务,则会出现问题。拥有这些任务将导致实体具有外部依赖性。 例如-限制用户最多只能参加3个小组。这将需要从group.JoinUser方法内部进行DB调用来验证这一点。 但是,实体依赖于某些外部服务/类这一事实在我看来并不那么好,也不是“自然的”。 在DDD中处理此问题的正确方法是什么?

5
ORM是否启用富域模型的创建?
在我的大多数项目中使用Hibernate大约8年之后,我进入了一家不鼓励使用Hibernate并希望应用程序仅通过存储过程与DB进行交互的公司。 在执行了几周之后,我无法为我将开始构建的应用程序创建丰富的域模型,并且该应用程序看起来像(可怕的)事务脚本。 我发现的一些问题是: 由于存储过程仅加载最少的数据,因此无法导航对象图,这意味着有时我们具有具有不同字段的相似对象。一个示例是:我们有一个存储过程来从客户那里检索所有数据,另一个存储过程是从客户那里检索帐户信息以及一些字段。 许多逻辑最终出现在帮助器类中,因此代码变得更加结构化(实体用作旧的C结构)。 令人讨厌的脚手架代码,因为没有框架可以从存储过程中提取结果集并将其放入实体中。 我的问题是: 有没有人遇到过类似情况,并且不同意存储过程方法?你做了什么? 使用存储过程有实际好处吗?除了“没有人可以发布弃用表”这一愚蠢的观点之外。 有没有一种使用存储过程来创建富域的方法?我知道有可能使用AOP将DAO /存储库注入到实体中,从而能够导航对象图。我不喜欢这个选项,因为它非常接近伏都教。 结论 首先,谢谢大家的回答。我得出的结论是,ORM无法启用Rich Domain模型的创建(如某些人所述),但是它确实简化了(通常是重复的)工作量。以下是对结论的更详细说明,但并不基于任何硬数据。 大多数应用程序请求并将信息发送到其他系统。为此,我们在模型术语(例如,业务事件)中创建抽象,然后域模型发送或接收事件。事件通常需要来自模型的一小部分信息,而不是整个模型。例如,在一家网上商店中,支付网关会向用户收取一些用户信息和总计费用,但不需要购买历史记录,可用产品和所有客户群。因此,该事件具有少量特定的数据集。 如果我们将应用程序的数据库作为外部系统,则需要创建一个抽象,以允许我们将域模型实体映射到数据库(如NimChimpsky所述,使用数据映射器)。明显的区别是,现在我们需要为每个模型实体手工映射到数据库(旧模式或存储过程),而且还有一个痛苦,因为两个实体不同步,所以一个域实体可能会部分映射数据库实体(例如,仅包含用户名和密码的UserCredentials类映射到具有其他列的Users表),或者一个域模型实体可能映射到多个数据库实体(例如,如果存在一对一一张表上的映射,但我们只想将所有数据归为一类)。 在只有几个实体的应用程序中,如果不需要横向实体,那么额外的工作量可能很小,但是当有条件需要横向实体时,额外的工作量就会增加(因此,我们可能想实现某种“懒惰”正在加载”)。随着应用程序具有更多实体的发展,这项工作只会增加(我感觉它会非线性地增加)。我在这里的假设是,我们不会尝试重塑ORM。 将数据库视为外部系统的一个好处是,我们可以围绕以下情况编写代码:需要运行两种不同版本的应用程序,其中每个应用程序都有不同的映射。在连续交付生产的情况下,这变得更加有趣……但是我认为ORM在较小程度上也是可行的。 我将不考虑安全性,因为开发人员即使没有访问数据库的权限,也可以通过注入恶意代码来获取存储在系统中的大部分(即使不是全部)信息。我不敢相信我忘了删除记录客户信用卡详细信息的行,亲爱的上帝!)。 小更新(6/6/2012) 存储过程(至少在Oracle中)阻止执行诸如零停机时间连续交付之类的操作,因为对表结构的任何更改都将使过程和触发器无效。因此,在更新数据库期间,应用程序也将关闭。Oracle 为这种称为基于版本的重新定义提供了解决方案,但是我向该功能询问过的少数DBA提到,该功能实施得很差,他们不会将其放入生产数据库中。

3
DDD应用程序服务和REST API之间的概念不匹配
我正在尝试设计一个具有复杂业务域和支持REST API(严格来说不是REST,而是面向资源)的应用程序。我想出一种以面向资源的方式公开域模型的方法时遇到一些麻烦。 在DDD中,域模型的客户端需要通过过程“应用程序服务”层来访问由实体和域服务实现的任何业务功能。例如,有一个具有两种方法来更新User实体的应用程序服务: userService.ChangeName(name); userService.ChangeEmail(email); 此应用程序服务的API公开命令(动词,过程),而不是状态。 但是,如果我们还需要为同一应用程序提供RESTful API,则有一个用户资源模型,如下所示: { name:"name", email:"email@mail.com" } 面向资源的API公开状态,而不是命令。这引起了以下问题: 根据REST API上的每个属性,每个针对REST API的更新操作都可以映射到一个或多个Application Service过程调用。 对于REST API客户端,每个更新操作看起来都是原子操作,但是并不是那样实现的。每个应用程序服务调用被设计为一个单独的事务。在资源模型上更新一个字段可能会更改其他字段的验证规则。因此,我们需要一起验证所有资源模型字段,以确保所有潜在的应用程序服务调用在开始创建之前都是有效的。一次验证一组命令要比一次执行简单得多。我们如何在甚至不知道存在单个命令的客户端上执行此操作? 以不同的顺序调用Application Service方法可能会产生不同的效果,而REST API看起来没有什么区别(在一个资源内) 我可以想出更多类似的问题,但是基本上它们都是由同一件事引起的。每次调用应用程序服务后,系统状态都会更改。什么是有效更改的规则,即实体可以执行下一个更改的一组操作。面向资源的API试图使它们看起来都像原子操作。但是跨越这个鸿沟的复杂性一定要到某个地方,而且看起来是巨大的。 另外,如果UI更加面向命令(通常是这种情况),那么我们将不得不在客户端的命令和资源之间进行映射,然后再在API端进行映射。 问题: 所有这些复杂性是否应该仅由(厚)REST到AppService映射层处理? 还是我对DDD / REST的理解缺少什么? REST是否对于在一定程度(相当低)的复杂度下公开域模型功能不可行?

3
关系数据库和迭代开发
在许多软件开发方法中,例如敏捷方法论,领域驱动设计和面向对象的分析与设计,都鼓励我们采用一种迭代方法进行开发。 因此,我们不应该在第一次开始从事该项目时就正确完成我们的领域模型。相反,随着时间的流逝,我们重构模型,因为随着时间的流逝,我们对问题领域有了更深入的了解。 除此之外,即使我们已经尝试过建立一个完美的模型(我已经确信这很困难),需求也可能会发生变化。软件打完已经被部署到生产,最终用户可能会注意到,有一定要求的不完全了解,或者更糟的是,一些要求失踪了。 这里的要点是,在软件部署之后,我们可能最终需要更改模型。如果发生这种情况,我们就会遇到问题:生产数据库中的用户数据很重要,并且已经以旧模型的格式进行了拟合。 如果代码设计不当且系统很大,则更新代码可能是一项艰巨的任务。但这可以随着时间的推移而完成,我们拥有类似Git的工具,可以帮助我们做到这一点,而不会损坏可投入生产的版本。 另一方面,如果模型改变,类的属性消失或其他原因,则数据库也应改变。但是我们有一个问题:已经有不能丢失的数据,已经为旧模型格式化了。 关系数据库似乎是阻碍我们进行迭代开发甚至在最终用户需要时更新软件的障碍。 我已经使用的一种方法是编写一个特殊的类,该类将旧的数据库表映射到新的数据库表。因此,这些类选择旧格式的数据,将其转换为新模型使用的格式,然后保存到新表中。 这种方法似乎不是最好的方法。我在这里的问题是:是否存在任何众所周知的和推荐的方法来协调迭代开发与关系数据库?

2
贫血域模型和域服务注入
所述贫血域模型被描述为由Martin Fowler在领域驱动设计的反模式。为了在域模型上具有业务逻辑,通常使用域服务。但是Vaughn Vernon认为将域服务注入域模型是有害的(请参阅“实施域驱动设计,第387页”)。 我认为这些意见是矛盾的,这是真的吗?如何兼顾这两点? 是注入域服务与贫血域模型和普通域服务相比真的富域模型吗?

4
持久性如何适合纯功能性语言?
使用命令处理程序处理持久性的模式如何适合于一种纯函数式语言,在这种语言中,我们希望使与IO相关的代码尽可能的薄? 当以面向对象的语言实现域驱动设计时,通常使用命令/处理程序模式执行状态更改。在这种设计中,命令处理程序位于您的域对象之上,并负责无聊的与持久性相关的逻辑,例如使用存储库和发布域事件。处理程序是您域模型的公开面孔;诸如UI之类的应用程序代码在需要更改域对象的状态时会调用处理程序。 C#中的草图: public class DiscardDraftDocumentCommandHandler : CommandHandler<DiscardDraftDocument> { IDraftDocumentRepository _repo; IEventPublisher _publisher; public DiscardDraftCommandHandler(IDraftDocumentRepository repo, IEventPublisher publisher) { _repo = repo; _publisher = publisher; } public override void Handle(DiscardDraftDocument command) { var document = _repo.Get(command.DocumentId); document.Discard(command.UserId); _publisher.Publish(document.NewEvents); } } 该document域对象是负责执行业务规则(如“用户应该有权丢弃文档”或“你不能放弃一个已经被丢弃的文件”),并产生我们需要发布域事件(document.NewEvents会是一个IEnumerable<Event>,并且可能包含一个DocumentDiscarded事件)。 这是一个不错的设计-易于扩展(您可以通过添加新的命令处理程序来添加新用例,而无需更改域模型),并且不知道对象的持久化方式(可以轻松地将NHibernate存储库换成Mongo存储库,或将RabbitMQ发布者替换为EventStore发布者),这使得使用伪造品和模拟进行测试变得容易。它还遵循模型/视图分离-命令处理程序不知道批处理作业,GUI或REST API是否正在使用它。 在Haskell这样的纯功能语言中,您可以大致像这样对命令处理程序进行建模: newtype CommandHandler = CommandHandler {handleCommand :: …

8
不可变对象和DDD是否在一起?
考虑使用DDD的系统(同样:使用ORM的任何系统)。实际上,在几乎每个用例中,任何系统的重点都是操纵这些域对象。否则,就没有真正的效果或目的。 修改一个不可变的对象会导致该对象持久化后生成新记录,这会在数据源中造成巨大的膨胀(除非您在修改后删除了以前的记录)。 我可以看到使用不可变对象的好处,但是从这个意义上讲,我从来没有看到使用不可变对象的有用案例。这是错的吗?

3
如何处理命令中的验证后错误(DDD + CQRS)
例如,当您提交注册表单时,您必须在Domain Model(WriteModel中CQRS)中检查表单是否处于有效状态(例如,电子邮件地址语法,年龄等)。 然后,您创建一个Command,并将其发送到Command Bus。 我知道Commands不应该返回任何东西。 那么,您如何处理超出范围的错误Command Bus呢?(例如,用户在1秒钟之前使用进行了注册username/email)。 您如何知道该命令失败,以及如何知道该错误?

3
MVVM,DDD和WPF分层应用程序项目结构指南
我正在尝试在VS中设置我的应用程序的结构,我想“尝试”并在将来对其进行合理的证明。该应用程序将是对旧的Winform应用程序的WPF重写,该应用程序没有遵循任何约定。没有层,层,首字母缩略词等。 这是一个相当大的企业应用程序。我计划将Linq To SQL用作数据库,并且很可能始终是MS SQL。我也有一个现有的技能。 我想尽可能地遵循MVVM和DDD,但是结合使用它们时,我对应用程序的结构感到困惑。让我尝试通过一些示例进行说明。 当我遵循MVVM时,我的文件夹结构可能如下所示: Views Models ViewModels Helpers 但是这如何适合于简单的DDD分层方法,其中我的项目结构可能类似于此: MyApp.UI MyApp.Domain MyApp.Data 我应该将其Models放在“域”层中还是有3种说法Person?这就引出了另一个问题,即我将数据库对象的存储库和映射放在哪里?我会假设数据... Views我会进入UI,但ViewModels也会吗? 最后,我将业务逻辑嵌入哪里? 我在CodePlex,DDD示例中发现了以下内容,虽然对您有所帮助,但似乎对Web应用程序有所帮​​助,但这似乎是我的无知。 不要误会我的意思,我知道我可以拥有尽可能多的文件夹,并可以随意命名。我试图弄清楚在哪里放置东西,以便可以扩展,而不是那些地方必须叫什么。 我的问题的核心可能会这样显示。 我有tblPerson产生的物件*.dbml。这很明显,将属于我的“数据”层。 现在,我将拥有Model,DTO,Domain Model或在称为的单独Layer(project?)中调用的任何东西Person。我需要一个Mapper用于Person对tblPerson那个我不知道放在哪里。 然后,我将拥有一个ViewModel,例如,EditPerson它将拥有它自己的属性,Person但可能还会更多。 最后,我将有一个绑定到该ViewModel的View。 需要明确的是,我的假设和猜测已填满该段,我希望有人能为我打气或向他们提供见解,这样从现在开始的6个月到一年内,我不会踢得比自己需要的更多。

2
DDD-Lite是一种用于依赖注入的模式语言吗?
我偶然发现了格雷格·杨(Greg Young)的演讲DDD项目失败的7个原因,他在7:20提到了他称为DDD-Lite的内容。 概括地说,他基本上说有些人将DDD用作模式语言(实体,存储库,值对象,服务等),而没有做任何其他与DDD相关的事情。他假设.Net中60%或更多的域模型是DDD-Lite。他认为DDD-Lite基本上是在围绕依赖注入构建一种语言,而您实际上并不需要这样做。他说要么完全做DDD,要么做一些简单的事情。否则,他声称一个人正在为构建良好的抽象而进行所有这些工作,但是没有任何实际的好处。 我必须承认,我对DDD的了解不尽如人意,并且还没有尝试使用它。我也没有读过Eric Evan的书。我对依赖注入更加感兴趣,关于此主题的许多书籍和博客都使用Eric Evans的DDD书籍中的术语和参考概念。这是我接触过DDD概念的地方。我一直在阅读的书籍包括: .NET中的依赖注入 Microsoft .Net:为企业设计应用程序 .NET中的Brownfield应用程序开发 如果要进行依赖注入,那么比“ DDD-Lite”更简单的选择是什么?在我看来,建立良好的抽象非常有用,无论人们是否以“ DDD-Lite”方式使用DDD中的概念。(请参阅Mark Seemann的博客文章:接口不是抽象,而是迈向更好的抽象)。我很难相信每个进行依赖注入的人也都在做(或需要做)完整的DDD。我是否以某种方式误解了格雷格·扬(Greg Young)关于DDD-Lite的论点?

5
服务在SOA中共享数据库是不好的做法吗?
我最近一直在阅读Hohpe和Woolf的企业集成模式,Thomas Erl的一些关于SOA的书,以及观看Udi Dahan等人的各种视频和播客。在CQRS和事件驱动系统上。 我工作场所中的系统耦合度很高。尽管理论上每个系统都有其自己的数据库,但是它们之间有很多连接。实际上,这意味着所有系统都使用一个庞大的数据库。例如,只有一张客户数据表。 我读过的大部分内容似乎都建议对数据进行非规范化处理,以便每个系统仅使用其数据库,并且使用消息传递将对一个系统的任何更新传播到所有其他系统。 我认为这是在SOA中强制执行边界的一种方法-每个服务都应该有自己的数据库,但是我读到以下内容: /programming/4019902/soa-joining-data-across-multiple-services 这表明这是错误的做法。 隔离数据库似乎是解耦系统的一种好方法,但是现在我有点困惑。这是一条好路吗?是否曾经建议过在SOA服务,DDD绑定上下文,应用程序等上隔离数据库?

3
使用DDD和CRQS时,每个命令应该恰好是一个事件吗?
我正在寻找一种设计约定通用的ddd应用程序的方法。 说一个聚合“客户端”有一个定义为“ FillProfile”的命令。它将在逻辑上引发事件“ ProfileFilled”。 在某些情况下,命令会引发的事件多于一个事件,或者命令会基于某种逻辑引发不同的事件吗?还是这始终是1-1的关系(1个命令将始终不引发任何事件,或者仅引发给定类型的单个事件)。 我之所以这样问是因为,如果这是事实,那么命令将始终引发同一事件,那么我可以基于该事实建立约定系统。我知道“ RaiseEvent”将导致“ EventRaised” ...

2
DDD有界的上下文和域?
我一直在一个相对复杂的应用程序中,该应用程序具有10多个数据库表(聚合,实体/值对象)并应用DDD。此时,它似乎基本上是DDD-Lite,这意味着有应用程序/域服务,域模型(实体,值对象)和存储库。 我拿起一本书《实现DDD》,他首先提到的是DDD-Lite和Bounded Contexts和Domain Events缺失,这是开始DDD时常见的第一个错误。 目前,我已经尝试通过聚合关系来组织域模型,并使用名称空间进行演示。 我没有看到与将域模型项目划分到单独的有界上下文中有关的好处/缺点(尚未)。也许稍后会变得很明显,但我希望获得有关绑定上下文(以及可能绑定到子域等的一些现实生活)的反馈。

1
域驱动设计对于不是那么复杂的域有用/有用吗?
在评估工作中的潜在项目时,我建议对它的对象模型使用领域驱动的设计方法可能会更有利。该项目没有一个过于复杂的领域,所以我的同事把这个扔给了我: 有人说,DDD在存在复杂域模型的情况下是有利的(“ ...只要我们在复杂,复杂域中运行,都适用” Eric Evans)。 我所迷惑的是-您如何定义域的复杂性?是否可以通过域模型中聚合根的数量来定义?对象交互中的域是否复杂? 我们正在评估的领域与在线发布和内容管理相关。

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.