在领域驱动设计 (DDD) 中建模查找表的实用方法是啥?

     2023-02-16     81

关键词:

【中文标题】在领域驱动设计 (DDD) 中建模查找表的实用方法是啥?【英文标题】:What is a practical way to model lookup tables in Domain Driven Design (DDD)?在领域驱动设计 (DDD) 中建模查找表的实用方法是什么? 【发布时间】:2010-10-22 22:47:41 【问题描述】:

我刚刚学习 DDD(Eric Evans 的书在我面前打开),我遇到了一个我找不到答案的问题。当您只是想获得一个简单的查找记录列表时,您在 DDD 中会做什么?

例如

员工 ID:123 员工姓名:John Doe 州:阿拉斯加(下拉) 县:瓦西拉(下拉列表 -- 将根据州过滤)。

例如,假设您有一个 Employee 域对象、一个 IEmployeeRepository 接口和一个 EmployeeRepository 类。 UI 将使用它来显示员工列表和个人详细信息。在 UI 中,您希望为员工居住的州和县使用下拉菜单。可用县将根据选择的州进行过滤。

不幸的是,数据库表和 UI 看起来非常不同。在 tblEmployees 中,它包含州代码=AK 和县代码=02130,而不是州和县名称。

旧方法(在我开始这个 DDD 任务之前)非常简单,只需创建 2 个查询并使用 DataReader 填充下拉列表。下拉菜单中显示的下方是值,它会自动在表单帖子中使用。

但是,对于 DDD,我不确定您应该如何执行此操作。我首先创建了州和县对象以及存储库和存储库接口。但是,编写 4 个类 + 2 个接口和 hbm.xml 文件中的管道 + Employee 业务对象对于 2 个下拉列表的 2 个查询似乎有点过头了。必须有更好的方法,不是吗?我不会很快更改州或县表中的记录,即使我这样做了,也不会通过此应用程序进行。因此,如果不需要的话,我真的不想为州和县创建业务对象。

我看到的最简单的解决方案是创建一个辅助类,其中包含返回字典的方法,例如 GetStatesAll()、GetState() 和 GetCounties() 以及 GetCounty(),但从 DDD 的角度来看这是错误的。

请帮忙。如何使用 DDD 而不过度设计几个简单的查找?

最终解决方案 我想我终于通过经验找到了答案,就是把GetStates()方法放到自己的Data Access类中,虽然不是repository类。由于我只进行只读访问,因此我将其放入结构 DTO 中。由于数据库很小,我将它们完全放在一个类中,就像下面描述的 Todd。

我的结论:

    查找表绝不是值对象,因为查找表总是有一个标识。如果他们没有身份,你就会有重复,这没有多大意义。 只读查找表可以有存储库,但可能不需要。存储库的目标是通过强制仅通过聚合访问来降低复杂性。通过汇总为您提供了一种确保可以强制执行业务规则的方法,例如如果您没有汽车则不添加轮胎。 如果您允许对查找表进行 CRUD 维护,则查找表拥有自己的存储库是有意义的。 我最终将代码存储为结构这一事实并没有使它们成为“值类型”。 Fowler 在 POEAA 中说结构是一种值类型。没错,结构是不可变的,这就是 Fowler 说它们是“值类型”的原因,但我使用它们的方式不同。我使用结构作为一种轻量级的方式来传递我从未计划在初始创建后更改的 DTO。事实上,我使用的结构确实具有标识,但由于它们是只读的,它们作为结构工作。 我一直在使用的一种我在其他地方看不到的模式是使主键字段不可变。它们由构造函数设置,但它们是只读的(不是私有访问器),一旦创建对象就不能更改。

【问题讨论】:

您找到解决方案了吗?如果是...请在下面发布...谢谢 优秀的总结。这种跟进太少见了。 【参考方案1】:

好吧,我前段时间读了Mathias Verraes 的一篇文章,谈到了这个here。他谈到了将模型中的值对象与服务于 UI 的概念分开。

当被问及是否将国家建模为实体或值对象时引用文章:

将国家建模为 实体并将它们存储在数据库中。但在大多数情况下,那 过于复杂的事情。国家不会经常变化。当一个 国家名称的变化,实际上,出于所有实际目的, 新国家。如果有一天一个国家不存在了,你不能 只需更改所有地址,因为国家可能被分割了 进入两个国家。

他提出了一种不同的方法来引入一个名为AvailableCountry的新概念:

这些可用的国家可以是数据库中的实体,也可以是数据库中的记录 JSON,甚至只是代码中的硬编码列表。 (这取决于 企业是否希望通过 UI 轻松访问它们。)

<?php

final class Country

    private $countryCode;

    public function __construct($countryCode)
    
        $this->countryCode = $countryCode;
    

    public function __toString()
    
        return $this->countryCode;
    


final class AvailableCountry

    private $country;
    private $name;

    public function __construct(Country $country, $name)
    
        $this->country = $country;
        $this->name = $name;
    

    /** @return Country */
    public function getCountry()
    
        return $this->country;
    

    public function getName()
    
        return $this->name;
    



final class AvailableCountryRepository

    /** @return AvailableCountry[] */
    public function findAll()
    
        return [
            'BE' => new AvailableCountry(new Country('BE'), 'Belgium'),
            'FR' => new AvailableCountry(new Country('FR'), 'France'),
            //...
        ];
    

    /** @return AvailableCountry */
    public function findByCountry(Country $country)
    
        return $this->findAll()[(string) $country];
    

所以似乎有第三种解决方案,即将查找表建模为值对象和实体。

顺便说一句,请确保您在 cmets 部分查看 some serious discussions 关于这篇文章的信息。

【讨论】:

【参考方案2】:

您可能想研究Command Query Separation 的概念。我不会担心查找值的类型化存储库,但我仍然可能会在数据集等上使用 DTO 类型类...

您可能想花一些时间阅读 Greg Young 从 this one 到现在的博客。他没有专门谈论填充查找数据,但他经常谈论不通过存储库上的类型化方法处理应用程序的读取/报告功能。

【讨论】:

谢谢。我不确定这是一个确切的答案,但它让我走上了正确的轨道,即“检索只读数据的最佳方法是什么”。【参考方案3】:

使用 DDD 我有以下类似的东西:

interface IAddressService

  IList<Country> GetCountries ();
  IList<State> GetStatesByCountry (string country);
  IList<City> GetCitiesByState (string state);
  // snip

Country、State 和 City 是来自数据库中查找表的值对象。

【讨论】:

如果参数是国家和州而不是字符串会更好。【参考方案4】:

如果您想学习如何进行 DDD 而又不使其过于复杂,那您就读错书了。 :-)

您提出的最简单的解决方案可以满足您的需求。将地址数据封装在业务对象中可以根据您的应用程序需求简单或复杂。例如,State 对象与 County 具有一对多的关系,因此如果您选择以这种方式建模,那么 Employee 真的只需要引用一个 County。如果需要,我只会介绍这种复杂性。

此外,我认为为存储库定义接口不会有什么好处,除非确实有可能为对象创建多个存储库。

【讨论】:

我的理解是,如果你没有为你的存储库使用接口,你并没有真正在做 DDD。为您的存储库创建接口不是更容易让您进行模拟吗?无论如何,在 Visual Studio 中,从您设计的类中生成一个接口需要 1 秒,所以我并没有真正看到使用接口的缺点。谢谢。 如果你只有一个实现接口的类,那么接口的意义何在?具体的类和接口一样可模拟。我确实认为接口对于可能具有多个实现的服务类以及使用依赖注入框架的服务类很有价值。 我还认为从类中提取接口是一种落后的方法——应该在实现之前设计契约。通过这种方式,接口确实有利于单元测试,因为可以在任何具体实现编码之前使用模拟接口创建测试。 只有一个类的接口是为了能够测试它。然后你可以把它存根,嘲笑它......如果你喜欢就吃它......我同意约翰......你做得很好......继续 "具体类与接口一样可模拟。"这不是真的。至少,您必须使属性虚拟化以使用最小起订量来模拟它们,我认为这会掩盖您的意图。我认为您最好将接口作为构造函数依赖项。更坚实。【参考方案5】:

州和县不是实体,而是价值对象。它们不是您系统的主题。你说你以前处理这些的方式是可以的。根据域模型状态的变化,您何时会更改数据库中的州或县记录?不,所以这些不需要存储库。

【讨论】:

好的,这有点道理。让我有些失望的是我对对象的 ID 字段感兴趣,但这并不一定使它们成为“实体”,是吗。你把所有值对象的创建放到哪里去了?进入一个全局 ValueObjectFactory 类?有最佳做法吗? 我同意@John 的观点:像 County 和 State 这样的查找表不是值对象,原因很简单:它们有 ID,它们由 ID 引用。该模型可以有一个地址值对象,该对象将引用县和州。只是为了强调 VO 可以引用实体这一事实。

领域驱动实战-支付系统

参考技术A在Airwallex,领域驱动设计(DDD)方法被用来指导我们的工程师如何对复杂的业务问题和系统设计建模。在这篇博客中,我们提供了一个全面的工作流,我们使用DDD模式进行建模,然后对支付系统进行落地。全球支付系统是... 查看详情

什么是ddd(领域驱动设计)?

领域驱动设计的基本概念领域驱动设计作为一个针对大型复杂业务系统的领域建模方法体系(不仅限于面向对象的领域建模),它改变了传统软件开发工程师针对数据库建模的方式,通过面向领域的思维方式,... 查看详情

浅谈我对ddd领域驱动设计的理解

目录从遇到问题开始DDD切入点1-理解概念什么是领域(Domain)?什么是设计(Design)?什么是驱动(Driven)?概念总结:DDD切入点2-理解领域、拆分领域、细化领域理解领域知识是基础拆分领域细化子域DDD切入点3-领域模型设计领... 查看详情

ddd(领域驱动设计)思想解读及优秀实践

DDD(领域驱动设计)思想解读及优秀实践第1章领域驱动设计的背景和来源理解学习DDD的必要性,DDD能对我们带来什么帮助,它的核心思想和解决的痛点问题是什么,它的发展历史、现状和未来前景如何?... 查看详情

ddd领域驱动设计-ddd概览

参考技术A#DDD概览##启迪领域可以理解为业务,领域专家就是对业务很了解的人。限界上下文也就是微服务的边界,也可以理解为微服务,一个限界上下文=一个微服务。个人理解领域驱动设计就是微服务驱动设计,从战略上先进... 查看详情

学习:ddd领域驱动设计

DDD:Domain-drivenDesign(领域-驱动->设计)->领域驱动领域模型设计->领域模型驱动代码实现 摘自网络(汤雪华的博客)《概念总结》领域就是问题域,有边界,领域中有很多问题;任何一个系统要解决的那个大问题都对应... 查看详情

ddd领域驱动设计落地实践系列:初识ddd

引言笔者在经历的很多项目中都使用了DDD领域驱动设计进行架构设计,尤其是在业务梳理、中台规划以及微服务划分等方面,DDD是重要的架构设计方法论,对平时的架构设计有非常好的指导作用。从本文开始笔者将通... 查看详情

领域驱动设计

1.什么是领域驱动设计(DDD:DomainDrivenDesign)    领域驱动设计(DDD)是一种基于模型驱动的软件设计方式。它以领域为核心,分析领域中的问题,通过建立一个领域模型来有效的解决领域中的核心的复杂问题。领域... 查看详情

ddd领域驱动设计精要

 本文算是《领域驱动设计》这本书的读书笔记,加上自己的一些读后感。网上有很多这本书的读书笔记,但是都是别人的,不如自己总结的理解深刻。建议大家在读这本书时结合《实现领域驱动设计》一起看,同时,一定要... 查看详情

ddd领域驱动设计实践——domain层实现

...valueobject、domainevent、domainservice的职责,以及如何识别出领域中的这些对象,并附有具体的业务建模示例。相比于《领域驱动设计》原书中的航运系统例子,社交服务系统的业务场景对于大家更加熟悉,相信更好理解。本文是【D... 查看详情

ddd领域驱动设计-设计文档模板

...板:系统背景和定位需求描述系统用例图关键业务流程图领域语言整理,主要是整理领域中的各种术语的定义,名词解释领域划分(分析出子域、核心域、支撑域)每个子域的领域模型设计(实体、值对象、聚合、领域事件,需... 查看详情

领域驱动设计和实践

郭晨软件1511531610114什么是领域驱动设计(DDD)  2004年著名建模专家EricEvans发表了他最具影响力的书籍:《Domain-DrivenDesign:TacklingComplexityintheHeartofSoftware》(中文译名:领域驱动设计:软件核心复杂性应对之道),书中提出了领域驱... 查看详情

领域模型驱动设计(ddd)之模型提炼

...所深深困扰而无暇顾及软件的真正核心业务建模其实业务领域建模同样是一个比平台架构更复杂更需要学习的新的领域  相反在实践中我们技术人员在经过冗长的平台架构学习和实践后就匆忙开始项目开发这时是什么指导他们... 查看详情

领域驱动设计(domain-drivendesign)总结(代码片段)

1.领域驱动设计概述DDD是指“领域驱动设计”(Domain-DrivenDesign),是一种软件设计方法论,主要关注于解决复杂业务领域的建模和实现问题。DDD的核心思想是将业务领域作为设计的核心,将业务领域的概念和... 查看详情

谈谈ddd(领域驱动设计)(代码片段)

...组织了小红花的新一期分享快速搞定数字化项目——采用领域驱动设计(DDD)建设一个电商平台,听完池总的这个分享之后,我终于是把这两年重新热起来DDD(以下称为现代DDD)和我十几年前熟悉的DDD(以下称为... 查看详情

领域驱动设计(ddd)

领域驱动设计在互联网业务开发中的实践领域驱动设计,根本目的在于,系统的高内聚低耦合。通过拆分具体需求,生成核心上下文,各个支撑上下文,上下文之间低耦合。  查看详情

干净的领域驱动设计(DDD)是乌托邦吗? [关闭]

】干净的领域驱动设计(DDD)是乌托邦吗?[关闭]【英文标题】:Iscleandomain-driven-design(DDD)autopia?[closed]【发布时间】:2011-09-1016:04:21【问题描述】:我想我读过所有关于DDD以及如何在C#中应用它的书(3-5本书,不记得是几年前的... 查看详情

领域驱动设计(ddd)中简单易用的10种技巧

领域驱动设计(DDD),因非常适合与微服务进行配合而闻名,因《领域驱动设计》那本书的难懂而让人望而却步。其实《领域驱动设计》这本书讲的是:以领域为核心,在代码中体现领域的思想,开发人员和领域专家要紧密沟通。... 查看详情