Skip to content

记录 #1

@mind1949

Description

@mind1949

为什么需要领域驱动设计

贫血(失血)的事务性脚本,会丢失业务知识。
一开始使用贫血(失血)的事务性脚本还好,但随着业务逻辑日趋复杂,代码量在飞速增长,业务知识在不断丢失,最终很难通过阅读代码理解业务逻辑,项目的可构建性、可理解性飞速下降。
这个时候就轮到领域驱动设计登场了。领域驱动设计可以极大的减少、避免业务知识的丢失,从而更好的应对日趋复杂的业务逻辑。

个人对领域驱动设计的看法

领域驱动设计的确可以应对业务的复杂性,但是其包含的模式也需要根据具体的业务、团队的技术储备考虑是否采用。
例如聚合这个战术模式。严格来说每个聚合执行的操作都在一个单独的事务中,各个聚合之间通过领域事件完成合作,这就引入了复杂性,我觉得不如舍弃这个原则,直接在application service层开启请求级的事务,这样简单、可靠。

如何整理领域驱动设计

  • 是什么
  • 能解决什么问题
  • 有什么副作用
  • 在go中如何实现
  • 何时使用
  • 结合具体的业务场景做分析
  • 使用领域驱动设计的流程

战略

图片

  • 问题空间
  • 解决方案空间
  • 限界上下文(是一个语义、语境上的概念,可以理解为一种组件封装。限界上下文的拆分也符合组件的聚合、耦合原则)
  • 通用语言
  • 核心域
  • 业务驱动

子域(与限界上下文的关系。)
分类:核心域(sub domain)、支撑子域(supporting subdomain)、通用子域(generic subdomain)
应对复杂性:划分出核心域、支撑子域、通用子域,集中资源于核心域。

上下文映射cotext mapping(上下文的集成)

上下文间关系:

  • 合作(partnership)
  • 共享内核(sharedKernel)两个包组件是有可能共享同一个模型的
  • 客户供应商(customer-supplier)
  • 跟随者(conformist)
  • 防腐层(anticorruptionLayer)
  • 开放主机服务(OpenHostService)
  • 已发布语言(PublishedLanguage)
  • 各行其道(separateWay)
  • 大泥球(BigBallofMud)

上下文的集成方式:

  • rpc
  • rest
  • 消息(聚合发布领域事件)

战术

聚合(aggregate)

实体(entity)
值对象(value object)
聚合(名称一般就是根实体名称)

聚合是事务一致性的边界(1. 对每个聚合的修改都只能在单个独立的事务中完成;2. 若多个聚合要合作,则通过领域事件互相通知,完成合作)。

聚合设计的基本规则:

  • 在聚合边界内保护业务不变性(从这个角度来说聚合其实是为了简化业务操作,但是因为eager load有可能导致过多的内存消耗);
  • 聚合要设计的小巧(聚合设计的过大,可能不会方便多少,更有可能占用太多内存。根据业务上各个模型更新的是否是即时的,还是允许延迟的来考虑设计聚合。有时候通过仔细思考业务逻辑,会发现很多更新并不需要即时,都可以通过最终一致性做好);
  • 只能通过标识符引用其它聚合(避免聚合相互依赖,导致内存占用过高、编写复杂);
  • 使用最终一致性更新其它聚合(两个聚合通过发布、订阅领域事件完成合作,为了简单,统一都用外部的消息中间件完成。但这里并没有解释为何要不管是否在统一个进程内都要用最终一致性。我觉得可能是这样,如果一个团队已经把多个服务间的最终一致性玩的很六了,那么他们就会倾向于在同一个进程内也是用最终一致性,因为对他们来说已经很成熟可靠了,为什么不用统一的方式集成各个聚合呢);

领域事件(domain event)

为何要有领域事件?
如何命名领域事件?
如何在项目中完成领域事件的发布与订阅?

事件溯源(event sourcing)

事件风暴

通过事件风暴帮助理清业务,确定通用语言

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions