Questions tagged «domain-model»

域模型由对象,行为,关系和属性组成,这些对象,行为,关系和属性构成了作为开发重点的行业。


5
有了所有这些服务,我怎么会变得贫血呢?
我们在委托与封装业务逻辑之间划清界限的地方是什么?在我看来,我们委派的越多,我们变得越贫乏。但是,委派还促进重用和DRY主体。那么什么是合适的委托,我们的领域模型应该保留什么呢? 以以下问题为例: 授权。域对象应该负责维护其访问控制规则(例如CanEdit属性)还是应该委派给另一个单独负责管理访问的组件/服务,例如IAuthorizationService.CanEdit(object)?还是应该将两者结合?也许域对象具有CanEdit属性,该属性委派给内部IAuthorizationService来执行实际工作? 验证。与上述相同的讨论涉及验证。谁维护规则,谁负责评估规则?一方面,对象的状态应该属于该对象,而有效性是一个状态,但是我们不想重写用于评估每个域对象的规则的代码。在这种情况下,我们可以使用继承... 对象创建。工厂类与工厂方法与“更新”实例。如果我们使用单独的工厂类,则可以隔离和封装创建逻辑,但要以将对象的状态打开给工厂为代价。如果我们的域层在单独的程序集中,则可以通过公开工厂使用的内部构造函数来进行管理,但是如果存在多个创建模式,这将成为一个问题。而且,如果工厂所做的所有工作都在调用正确的构造函数,那么拥有工厂的意义何在? 类上的工厂方法消除了打开对象内部状态的问题,但是由于它们是静态的,因此我们无法像通过单独的工厂类那样通过注入工厂接口来打破依赖关系。 坚持不懈。有人可能会争辩说,如果我们的域对象要在向另一方委派执行授权检查的职责时将CanEdit公开给他人(IAuthorizationService),为什么在我们的域对象上没有执行相同操作的Save方法呢?这将使我们能够评估对象的内部状态,以确定是否可以在不破坏封装的情况下执行操作。当然,这要求我们将存储库实例注入到我们的域对象中,这对我来说有点气味,那么我们是否应该引发域事件并允许处理程序执行持久化操作? 看看我要去哪里? Rockford Lhotka对他为什么选择CSLA框架中的班级主管路线进行了精彩的讨论,而我对该框架有一些了解,可以看到他的业务对象以多种方式与域对象并行的想法。但是,我试图成为更好的DDD理想的坚持者,我想知道何时协作变得过多。 如果最终以聚合根为IAuthorizationService,IValidator,IFactory和IRepository,还剩下什么?是否具有将对象状态从“草稿”更改为“已发布”的Publish方法足以将类视为非贫血领域对象? 你的意见?

20
开发人员必须了解业务领域,还是该规范足够?
我在一家公司领域工作,因为该领域是电子领域的高科技,因此很难理解,但是它适用于复杂领域中的任何软件开发。 我工作的应用程序显示了很多信息,图表和度量标准,如果没有该领域的经验,这些信息,图表和度量标准将很难理解。开发人员使用规范来描述软件必须执行的操作,例如指定特定的图表必须显示此类指标,并且该指标是以下算术公式。 这样,开发人员并不真正了解业务以及他/为什么要执行此任务。如果规范确实很详细,那么可以这样做,但是当规范不是很详细时,或者当作者忘记了用例时,开发人员很难找到解决方案。 另一方面,对每个开发人员进行所有业务方面的培训可能会非常漫长且困难。 我们应该更加重视详细的规范(但众所周知,不存在完善的规范),还是应该培训所有开发人员以了解业务领域? 编辑:请记住您的回答,即该公司可以使用外部开发人员,并且对所有域的组建大约需要2周的时间

7
RESTful API是否倾向于鼓励贫血领域模型?
我正在一个项目中,我们试图将域驱动设计和REST都应用于面向服务的体系结构。我们不必担心100%符合REST。最好说我们正在尝试构建面向资源的HTTP API(Richardson REST成熟度模型的第2级)。但是,我们试图远离RPC样式的HTTP请求的使用,即,我们尝试根据RFC2616实现我们的HTTP动词,而不是使用POSTdo来实现IsPostalAddressValid(...)。 但是,对此的强调似乎是以我们尝试应用域驱动设计为代价的。只有GET,POST,PUT,DELETE和其他一些很少使用的方法,我们倾向于建立眉头服务和眉头服务往往有贫血的域模型。 POST:接收数据,对其进行验证,然后将其转储到数据中。GET:检索数据,然后将其返回。那里没有真正的业务逻辑。我们还在服务之间使用消息(事件),在我看来,大多数业务逻辑最终都围绕该消息构建。 REST和DDD是否处于某种程度的紧张状态?(或者我在这里误解了什么?我们是否可能在做其他错误?)是否有可能在面向服务的体系结构中构建强大的域模型,同时避免RPC样式的HTTP调用?

7
分别建模名字和姓氏
在设计新系统时,应该考虑哪些参数,并且必须将一个人的姓名存储为一个字段,或者将其分别存储为名字/姓氏? 单一领域的优点: 简单的用户界面 尝试输入一个名字很长的人的名字时没有歧义(通常不明显,这是姓氏/名字..) 处理标题时的复杂度较低(例如,无需单独输入“ MD”或“ Dr.”) 拆分字段的优点: 可以通过“亲爱的X先生”或“亲爱的朱莉”进行个性化交流 如果使用的Web服务需要单独的名字/姓氏,则可以轻松提供。 对于具有严格标识要求的任何行业(例如医疗,政府等)的更好选择 选择更加安全,因为您可以随时返回到单一字段替代方案 您是否看到上面未列出的任何其他参数? 更新:问题是,可以为每个解决方案列出哪些其他(未在问题中列出)参数。我认为提出意见而不是可能的利弊会以错误的方式推动讨论。每个开发人员都必须对这个问题做出决定,这个问题的目的是汇编一个非平凡的参数列表,可以在需要时进行评估。

4
除了男性和女性以外,是否存在性别模型的行业标准?
我正在建模一个数据库,该数据库应该用作启动公司的所有服务(如人员,用户,服务和商业数据,如优惠券,签名包等)的通用非功能性要求。 我正在考虑性别模型。在当今时代以及各国关于主观身份的法律不同的情况下,我是否应该考虑这一点,并为我的“个人”实体建模,而不仅仅是男性和女性选择? 选项包括:未定义,未回答,其他,跨性别...或我不知道的任何其他行业标准 ... 还是说LGBT人不是真正的男性或女性而冒犯了他们?

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对象吗?好处是您可以在描述域模型时谈论它们。但是,这是过度工程的一种情况,因为没有太多的验证。是否有一条规则描述了何时以及何时不消除原始的困扰,或者如果可能的话,您应该始终这样做吗? 我想我可以创建一个类型别名而不是一个类,这将有助于上面的第一点。

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

4
域与数据持久层中的干净架构验证?
我正在研究干净的软件,因此,我极大地重新思考了如何设计和编写软件的大量方法。 不过,我仍在努力处理业务规则,例如“保存某些项目的更新,首先加载我有权查看/编辑的所有项目列表,确认该项目在列表中,并且该商品类别当前未被锁定(以及其他规则等)。”,因为这是(复杂但非典型的)业务规则,因此应在应用程序域中进行处理,而不是将业务逻辑推送到db / persistence层。 但是在我看来,为了有效地检查这些条件,通常最好使用精心设计的数据库查询来最好地处理它,而不是将所有数据加载到应用程序域中... 如果不进行过早的优化,有什么推荐的方法或鲍勃叔叔的文章处理这个问题?还是他会说“在域中进行验证,直到出现问题”? 除了最基本的用例之外,我真的在努力寻找任何好的示例/样本。 更新: 大家好,谢谢您的答复。我应该更清楚一些,我已经写了很长时间(主要是Web应用程序)软件,并且肯定已经经历并同意您共同描述的所有主题(通过后端验证,一般不信任客户端数据仅在需要时才追求原始效率,但是在可用时承认db工具的优势,等等),并且已经经历了开发人员学习生命周期的“全部整合”以“构建具有N层应用程序的大型胖控制器”代码趋势,现在真的很喜欢研究干净/单一责任的样式等,这主要是由于最近有一些项目随着项目的发展以及对客户的进一步要求逐渐演变为笨拙且分布广泛的业务规则。 特别是,我在构建REST的API面向客户和内部使用功能的背景下,看着清新的风格建筑,其中许多业务规则可能是多比基本上你在网上看到每一个例子更复杂(甚至由Clean / Hex体系结构专家自己完成)。 所以我想我真的是在问(并且没有清楚地说出)Clean和REST api会如何放置在一起,这些天您看到的大多数MVC东西都带有传入的请求验证器(例如.NET中的FluentValidation库),但是其中许多我的“验证”规则不是“这是少于50个字符的字符串”,而是更多的,“如果某个相关对象当前已被Team X锁定,则该调用此用户案例/交互者的用户可以对数据集执行此操作吗?直到本月下旬,等等”。。。那些涉及深层次的验证,其中适用于业务域对象和域规则的数量。 我是否应该将这些规则分解为特定类型的Validator-object类型,以与每个用例交互器一起使用(受FluentValidator项目的启发,但涉及更多的业务逻辑和数据访问),我是否应该将验证视为类似于Gateway,我应该将那些验证放入网关(我认为这是错误的)等。 作为参考,我会去像几篇文章这样,但马蒂亚不讨论验证了。 但是我想我的问题的简短答案很像我接受的答案:“这绝非易事,要视情况而定”。

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

3
实体框架和避免贫血领域模型
在我们的业务逻辑中,我们有时会定义一些类似于以下内容的方法: User.ResetCourse(Course courseToReset) 问题在于用户和课程都是实体框架代理对象。这意味着当我们在“用户”或“课程”上命中导航属性时,可能会严重打击数据库,因为这些对象不可查询,因此可以正常地遍历它们。 为了解决这个问题,我们将签名更改为: User.ResetCourse(MyDBContext db, Course courseToReset) 这意味着我们可以直接查询数据库以有效地进行所需的更改,但是将Database上下文传递给业务对象似乎太错误了。 后来我们向用户迁移了服务层,这意味着我们拥有以下内容: CourseService.ResetForUser(Course courseToReset, User forUser) 该服务引用了创建时注入的DBContext,但是现在我们的业务对象只是没有行为的数据包(例如,Anemic Domain Model)。 我们如何避免这种情况?

6
实体方法调用上的DDD注入服务
问题的简短格式 在DDD和OOP的最佳实践中,是否可以在实体方法调用上注入服务? 长格式示例 假设我们在DDD中有一个经典的Order-LineItems案例,其中有一个称为Order的域实体,它也充当聚合根,并且该实体不仅由其Value Objects组成,而且还包含Line Item的集合实体。 假设我们希望在应用程序中使用流利的语法,以便我们可以做类似的事情(请注意第2行中的语法,在此称为getLineItems方法): $order = $orderService->getOrderByID($orderID); foreach($order->getLineItems($orderService) as $lineItem) { ... } 我们不想将任何LineItemRepository注入OrderEntity,因为这违反了我能想到的几个原则。但是,语法的流畅性是我们真正想要的,因为它易于阅读和维护以及测试。 考虑下面的代码,指出该方法getLineItems中OrderEntity: interface IOrderService { public function getOrderByID($orderID) : OrderEntity; public function getLineItems(OrderEntity $orderEntity) : LineItemCollection; } class OrderService implements IOrderService { private $orderRepository; private $lineItemRepository; public function __construct(IOrderRepository $orderRepository, ILineItemRepository $lineItemRepository) { $this->orderRepository …

4
查找表:它们是否在域模型中泄漏?
您正在建立一个跟踪公司的系统。这些公司有联系人。这些联系人通常都是专家,只回答某些类型的问题,例如帐单/付款,销售,订购和客户支持。 使用域驱动设计和洋葱架构,我使用以下类型对它进行了建模: 公司 有联络人 联系 有联系方式 ContactType(枚举) CompanyRepository(接口) EFCompanyRepository(在外部程序集中定义,使用EntityFramework,实现CompanyRepository) 对于如何为该应用程序建模数据库,我们的团队意见分歧。 A面:精益DDDers: 定义哪些ContactTypes对一个联系人有效是Domain的工作。向数据库中添加表以验证是否未保存未知的ContactType是域泄漏的迹象。它将逻辑传播得太远了。 将静态表添加到数据库和相应的代码是浪费的。在此应用程序中,数据库解决了一个问题:保留事物并将其还给我。编写一个额外的表和相应的CRUD代码很浪费。 更改持久性策略应尽可能容易。更改业务规则的可能性更大。如果我决定SQL Server花费太多,则不需要重新构建放在架构中的所有验证。 B面:传统主义者[这可能不是一个好名字。DBCentrists?]: 在数据库中存储没有读取代码就没有意义的数据是一个坏主意。报表和其他使用者必须自己重复值列表。 按需加载数据库类型字典的代码并不多。不用担心 如果更改的源是代码而不是数据,那么当更改时,我将不得不部署位而不是简单的SQL脚本。 哪一方都不对或错,但从长远来看,其中一方可能会更有效率,计算初始开发,错误等的开发时间。这是哪一方-还是有更好的折衷办法?其他编写这种样式的代码的团队做什么?

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.