ContextMapper 中文文档-v1.0.0

原创
2021/12/30 11:40
阅读数 604

ContextMapper 中文文档 v1.0.0

介绍

ContextMapper 是一个用于领域驱动设计(DDD)及其战略设计模式的模块化和可扩展的建模框架。 核心组件提供了一个DSL来创建具有这些DDD模式的上下文映射。 语言背后的模型及其语义规则表达了我们对DDD模式的解释,以及如何以简洁和一致的方式组合这些模式。 目前,Context Mapper是以Eclipse插件、Visual Studio Code扩展或者独立Java库版本的形式提供设计支持的。

快速开始

要使用ContextMapper建模,你需要安装Eclipse Plugin插件或者我们的Visual Studio Code扩展。 两者都提供对CML语言及其周边工具的支持。 请注意VS Code扩展还不支持我们所有的特性。 在这里你可以找到一个表,它说明了哪些特性在Eclipse和VS代码中可用。 VS代码扩展将很快支持所有功能!

VS 拓展方式

要在Visual Studio编辑器编写代码中使用ContextMapper,你只要通过市场安装或下载我们的扩展。

1 在VS Code Marketplace 搜索 ContextMapper

2 直接点击连接下载 https://marketplace.visualstudio.com/items?itemName=contextmapper.context-mapper-vscode-extension

Eclipse 插件

要在Eclipse 中使用ContextMapper 请安装Eclipse 插件, 在 Eclipse Marketplace 搜索 ContextMapper 安装即可,推荐使用Ecplipse 插件,毕竟大家都是从事java开发,要熟悉一些,除了安装Eclipse 插件之外,还需额外安装一个画图软件 graphviz 用于展示我们设计的上下文地图和uml 设计图,可搜索自行安装。

案例

下面的例子给你展示如何使用ContextMapper CML语言构建一个上下文映射,也可以参考 DDD 简单的工程应用,案例地址:https://github.com/citerus/dddsample-core

/** 
 * DDD Cargo应用程序采用CML建模。 注意,我们将应用程序分割成多个有边界的上下文  
 *
 */
ContextMap {
  contains CargoBookingContext
  contains VoyagePlanningContext
  contains LocationContext
  
  CargoBookingContext [SK]<->[SK] VoyagePlanningContext
  
  CargoBookingContext [D]<-[U,OHS,PL] LocationContext

  VoyagePlanningContext [D]<-[U,OHS,PL] LocationContext
}

如一个限界上下文在被ContextMap 使用前有详细的定义,你可以像如下声明并定义他们

BoundedContext LocationContext {
  Module location {
    Aggregate Location {
      Entity Location {
        aggregateRoot

        PortCode portcode
        - UnLocode unLocode
          String name
      }

      ValueObject UnLocode {
        String unLocode
      }

      ValueObject LocationShared {
        PortCode portCode
        - Location location
      }
    }
  }
}

创建CML工程

你可以使用任意工程类型,如java 工程,maven 工程,gradle 工程,但无论你选择哪一种工程类型,你的项目必须是Xtext项目,新建项目完成之后做如下配置即可。

右击项目--->configure--->Convert to Xtext Project

核心语法

CML语言基于以下策略DDD领域模型(或语义模型),

CML Language Semantic Model

下面的语义规则要么是由语言所基于的领域模型隐式给出的(见上面),要么是由相应的语义检查器强制执行的。 请注意,模型和语义规则表达了我们如何理解DDD模式,以及它们如何组合,以及我们自己如何在项目中应用DDD。 理论基础,文献定义有些模糊。

1,允许的上游角色:模式OHS和PL只能由上下游关系中的上游上下文实现。 上游上下文总是提供并公开特定的功能。 下游上下文使用和消费该服务,而不公开它自己的域模型的部分。 如果是这种情况,并且上游使用了这个功能,那么上游独立于下游的定义就会自相矛盾。

2,允许下游的角色: ACL和CF模式只能被上下游关系中的下游上下文应用。 这些模式解决了一个下游问题,即如何处理对另一个上下文的依赖。 必须集成上游模型的始终是下游上下文。

3,保护或遵循:ACL和CF模式不能联合应用,但提供了替代方案。 下游要么遵循(CF),要么使用ACL保护自己。

4,对称关系的完整性:OHS、PL、ACL和CF模式不适用于对称关系(伙伴关系和共享内核),因为这样做会导致与模式定义的矛盾。 在共享内核关系中,两个上下文通过共享代码(比如库)进行通信。 这两个上下文一起管理共享代码,这显然与前面提到的四个模式定义相矛盾。 OHS指的是一种定向的提供者/消费者行为,而这里的情况并非如此。 不需要公共的上下文间语言(PL),因为这两个上下文只是共享相同的模型。 ACL也不是必需的,因为两个参与者共享模型。 而且,由于上下文是一个共享的模型,因此任何上下文都不必遵循另一个上下文的模型。 在伙伴关系中,两种情况都相互依赖,这意味着他们只能共同成功或失败。

5,客户vs墨守成规:CF模式不适用于客户-供应商关系。 在客户-供应商关系中,客户对供应商有影响,至少可以就需求和实现的优先级进行谈判。 相比之下,墨守成规的人没有影响力,只是决定遵循上游提供的东西。

6,通用服务与定制服务:OHS 模式不适用于客户-供应商关系。 客户-供应商模式意味着相关团队紧密合作,意味着上游团队在计划中会尊重下游的需求,而OHS模式则表明上游团队决定以一种通用的方式实现一种API。 这是矛盾的,因为这是不太可能的,这样一个上游实施的OHS能够与所有的下游有一个紧密的客户-供应商关系。 从个人实践经验来看,客户与供应商的关系导致了单个客户的个性化需求。 一旦供应商实现了客户特定的API特性,根据模式定义,它就不再是OHS。

7,保护或合作:ACL模式不应该在客户-供应商关系中使用。 供应商的变更应与客户的需求同步。 保护应该是不必要的。 注意,这只是一个软规则,因为组合是可能的,但并不常见。 如果检测到违反规则,我们的工具将发出警告,而不是错误消息。

8,ORGANIZATIONAL Context Maps:ORGANIZATIONAL 类型的上下文映射(团队地图),只能包含团队类型的有界上下文。 这个检查器提供了团队地图的一致性。 在这样的地图上,一个有边界的上下文代表一个团队,而不是一个典型的有边界的上下文,比如一个系统、特性或应用程序。

9,SYSTEM LANDSCAPES: TEAM类型的有界上下文不能包含在SYSTEM_LANDSCAPE类型的上下文地图中。 这个检查器提供上下文映射的一致性。 这可以看作是规则8的相反情况。

10,团队实现边界上下文:只有团队才能实现边界上下文。 这个检查器确保了realize关键字只能用于TEAM类型的有界上下文。 将关键字添加到语言定义中是为了引用团队正在实现的边界上下文。 它对于典型的有边界上下文(系统、特性或应用程序)没有意义。

Context Map

Context Map是CML中最重要的元素,实现DDD上下文映射模式。 上下文映射包含有边界的上下文并定义它们的关系。

下面的CML代码片段演示了一个上下文映射的例子, 使用contains关键字,你可以向映射添加一个有边界的上下文。

ContextMap {
  type = SYSTEM_LANDSCAPE
  state = AS_IS

  contains CargoBookingContext
  contains VoyagePlanningContext
  contains LocationContext
	
  CargoBookingContext [SK]<->[SK] VoyagePlanningContext
}

或者,你可以只使用一个contains 关键字添加多个上下文用逗号隔开。

ContextMap DDDSampleContextMap {
  type SYSTEM_LANDSCAPE
  state AS_IS

  contains CargoBookingContext, VoyagePlanningContext, LocationContext

  CargoBookingContext [SK]<->[SK] VoyagePlanningContext
}

通过上面的例子可以看到,我们定义了一个上下文,名字可以自定义,上文中的 = 也可以省略。

一个 context map可以是下面两种类型的其中一种。

  • SYSTEM_LANDSCAPE
  • ORGANIZATIONAL

SYSTEM_LANDSCAPE 表示上下文映射的默认类型,代表有边界的上下文如软件系统(或应用程序)第二种类型 ORGANIZATIONAL (团队地图)说明了团队之间的关系,第二种的案例可以参考这里 https://github.com/ContextMapper/context-mapper-examples/tree/master/src/main/cml/insurance-example

state属性接受以下两个值,表示给定的上下文映射是表示当前的状态还是期望的状态。

  • AS_IS - 保持不变
  • TO_BE - 期望的或未完成的

上下文之间的关系

根据语义模型,我们提供以下对称关系(一般是平行关系)

  • Partnership (P) - 合作关系
  • Shared Kernel (SK) - 共享内核关系

非对称关系有以下两种类型(一般是垂直关系,上下游关系)

  • Upstream-Downstream - 上下游关系
  • Customer-Supplier (C/S) -客户-供应商关系,一种特殊的上下游关系

说明:客户与供应商的关系是一种上下游关系,在这种关系中,上游计划要考虑下游的需求。 因此下游团队的需求必须由上游团队来解决。 他们作为顾客和供应商相互作用。 一般的上下游关系不一定是客户与供应商的关系

上下游关系有以下三种定义方式

第一种

CargoBookingContext [D]<-[U] LocationContext
LocationContext [U]->[D] CargoBookingContext

第二种

LocationContext Upstream-Downstream CargoBookingContext
CargoBookingContext Downstream-Upstream LocationContext

第三种

CargoBookingContext <- LocationContext
LocationContext -> CargoBookingContext

上下文之间的关系角色

你可以在括号中进一步指定关系角色,如OHS (Open Host Service)或ACL (Anti-Corruption Layer)。 角色必须始终在U(上游)和D(下游)后面指定(如果没有省略的话)。

VoyagePlanningContext [D,ACL]<-[U,OHS,PL] LocationContext

因为箭头已经指明了哪个有界上下文是上游的,哪个是下游的,所以也可以直接在括号中添加关系角色,而不需要写U和D

VoyagePlanningContext [ACL]<-[OHS,PL] LocationContext
VoyagePlanningContext[ACL] Downstream-Upstream [OHS,PL]LocationContext

上下文之间的关系属性

你可以使用大括号,可以为一个关系指定额外的属性,有以下三种属性。

  • implementationTechnology - 实现技术方式
  • downstreamRights - 下游对上游有哪些治理权限和产生的影响
  • exposedAggregates - 声明上游暴露的聚合
VoyagePlanningContext [D,ACL]<-[U,OHS,PL] LocationContext {
    implementationTechnology = "RESTful HTTP"
}
VoyagePlanningContext [D,ACL]<-[U,OHS,PL] LocationContext {
    implementationTechnology = "RESTful HTTP"
    downstreamRights = VETO_RIGHT
}
VoyagePlanningContext [D,ACL]<-[U,OHS,PL] LocationContext {
    implementationTechnology = "RESTful HTTP"
    downstreamRights = VETO_RIGHT
    exposedAggregates = Customers, Addresses
}

downstreamRights 有以下值可选

  • INFLUENCER - 有影响的
  • OPINION_LEADER - 领导者
  • VETO_RIGHT - 否决
  • DECISION_MAKER - 决策者
  • MONOPOLIST - 独占

Bounded Context

限界上下文被定义在 CML(.cml)文件的根部,被引用在与其他上下文有关系的地方。

来看一个声明 BoundedContext 的例子

BoundedContext CustomerManagementContext implements CustomerManagementDomain {
  type = FEATURE
  domainVisionStatement = "The customer management context is responsible for ..."
  implementationTechnology = "Java, JEE Application"
  responsibilities = "Customers", "Addresses"
  knowledgeLevel = CONCRETE

  Module addresses {
    Aggregate Addresses {
      Entity Address {
        String city
      }
    }
  }
  Aggregate Customers {
    Entity Customer {
      aggregateRoot

      - SocialInsuranceNumber sin
      String firstname
      String lastname
      - List<Address> addresses
    }
  }
}

注意:在一个cml文件中 BoundedContext 命名必须是唯一的。

implements关键字指定由这个有界上下文实现哪个域或子域。 在implements关键字后面,您可以引用一组子域(逗号分隔)或一个顶级域。 参考这里了解如何指定子域 https://contextmapper.org/docs/subdomain/

参考如下属性赋值

BoundedContext ContextMapperTool refines StrategicDomainDrivenDesignContext {
  type FEATURE
  domainVisionStatement "Context Mapper provides a formal way to model strategic DDD Context Maps."
  implementationTechnology "Java, Eclipse"
}

可以使用 = 赋值 但也可以省略。

上面的示例还展示了如何让一个有界上下文优化另一个上下文(使用refines关键字)。 这个特性允许您创建某种类型的继承层次结构。

BoudedContext 类型

用 type 关键字你可以定义 BoundedContext 的类型,有如下类型可以选择。

  • FEATURE
  • APPLICATION
  • SYSTEM
  • TEAM

该类型提供了一个指示说明的含义,说明了限界上下文可能演化的原因。 它进一步允许您指定从哪个视角描述您的边界上下文。 FEATURE上下文是分析或早期设计抽象,采用功能场景视图。 应用上下文代表了更详细、更合理的设计和实现视图; 系统上下文添加了更物理的、面向流程和部署的视图 。

最后,您可能希望创建一个团队地图。 团队地图还允许您指定哪个团队正在实现哪个边界上下文(类型为FEATURE、APPLICATION或SYSTEM)。 注意,上下文映射类型必须是ORGANIZATIONAL才能指定团队映射。 上下文映射下描述了相应的语法,在这里可以找到一个团队映射的示例 https://github.com/ContextMapper/context-mapper-examples/tree/master/src/main/cml/insurance-example

Context Type Examples
FEATURE 这样一个有界的上下文代表了围绕一组功能特性(用户故事/用例)的边界。 例如,在保险场景中与客户管理相关的所有内容:创建客户、更新客户、更新客户地址等。
APPLICATION 这种类型的Bound Context从逻辑角度表示应用程序。 例如,一个保险公司的软件解决方案包含多个应用程序:用于客户的自助前端应用程序,用于管理客户和合同的后端系统,等等。 应用程序通常包含多个功能特性。 在(微)面向服务的体系结构中,每个(微)服务都可以看作是一个应用程序。
SYSTEM 系统边界上下文允许从更物理的角度(部署)对软件进行建模。 系统的例子:前端的单页应用程序,实现域逻辑的Spring Boot应用程序,保存数据的Oracle数据库,等等。 因此,应用程序通常由多个系统组成。
TEAM 一个开发团队也可以代表一个有限的上下文。 例如,在我们的保险例子中,多个团队负责软件的不同部分:客户前端团队、客户后端团队、合同团队等。

Partnership

伙伴关系模式描述了两个有界上下文之间的关系,并在CML文件中的上下文映射中使用 ,多指水平关系,而非上下游关系。

含义说明:指两者没有业务关联性,任何一方变动不会对另一方造成影响。

语法示例,有以下两种声明使用方式。

ContractsContext [P]<->[P] ClaimsContext {
  implementationTechnology = "Messaging"
}
ContractsContext Partnership ClaimsContext {
  implementationTechnology = "Messaging"
}

implementationTechnology 说明合作关系的实现方式。

Shared Kernel

共享内核关系模式描述了两个有界上下文之间的关系,并在CML文件中的上下文映射中使用,此关系多指没有业务逻辑的工具集。

含义说明:指两者没有业务关联性,共享内核方变动会对依赖方造成影响。

语法实例,有以下两种声明方式。

CargoBookingContext [SK]<->[SK] VoyagePlanningContext {
  implementationTechnology = "Java Library"
}
CargoBookingContext Shared-Kernel VoyagePlanningContext {
  implementationTechnology = "Java Library"
}

Customer/Supplier

客户/供应商关系模式描述了两个有界上下文之间的关系,并在CML文件中的上下文映射中使用,此关系多指垂直关系,一种特殊的上下游关系。

含义说明:参照上文关于上下文之间的关系描述 客户-供应商的关系。

语法示例,有以下两种声明方式。

CustomerSelfServiceContext [D,C]<-[U,S] CustomerManagementContext
CustomerManagementContext [U,S]->[D,C] CustomerSelfServiceContext
CustomerSelfServiceContext Customer-Supplier CustomerManagementContext
CustomerManagementContext Supplier-Customer CustomerSelfServiceContext

这四种变体在语义上都是等价的。 请注意,箭头->总是指向供应商(上游)到客户(下游),因此,表示影响流(供应商对客户有影响,但客户对供应商没有影响)。 在客户/供应商关系定义中,你也可以省略U(上游)和D(下游)规范,因为供应商始终是上游,而客户始终是下游。例如:

CustomerSelfServiceContext [C]<-[S] CustomerManagementContext

用冒号结尾,你可以给每个关系命名:

CustomerSelfServiceContext [D,C,ACL]<-[U,S,PL] CustomerManagementContext : Customer_Frontend_Backend_Relationship { // Relationship name is optional
  implementationTechnology = "RESTful HTTP"
}

在括号中,您可以进一步指定关系角色,如开放主机服务(OHS)或反腐败层(ACL)。 角色必须始终在U/S(上游/供应商)和D/C(下游/客户)标志后面指定,如上例所示。 在声明体中,可以指定实现技术。

如果您使用“Customer-Supplier”或“Supplier-Customer”关键字而不是箭头,那么角色的声明是等价的,但是没有C/D和S/U:(请注意,无论您在括号前或后面写空格,或两者都写空格,都没有关系)

Conformist

墨守成规模式描述了两个有界上下文之间的关系,在CML文件中的上下文映射中使用,属于上下游关系。

含义说明:确定为上游关系,且不再发生任何改变。

语法实例

PolicyManagementContext [D,CF]<-[U,OHS,PL] CustomerManagementContext {
  implementationTechnology = "RESTful HTTP"
}

Open Host Service

开放主机服务模式描述了两个有界上下文之间的关系,在CML文件中的上下文映射中使用。

含义说明:开放主机服务是一种定向的提供者-消费者模式,也属于上下游关系。

语法示例

PrintingContext [U,OHS]->[D,ACL] PolicyManagementContext {
  implementationTechnology = "SOAP"
}

Anticorruption Layer

开放主机服务模式描述了两个有界上下文之间的关系,在CML文件中的上下文映射中使用。

含义说明:为了避免上游的OHS提供的服务而导致下游的污染。

语法示例

DebtCollection [D,ACL]<-[U,OHS,PL] PrintingContext {
  implementationTechnology = "SOAP"
}

注意:防腐层一般只是存在上下游关系中,但也可以应用在 客户-供应商的关系中,只是会有警告不影响语法检测。

Published Language

发布语言模式描述了两个有界上下文之间的关系,在CML文件中的上下文映射中使用。

含义说明:通常在上下文上下游关系中如有开放主机服务,会有发布语言的声明,如服务通信方式是 restful 还是 mq。

语法示例

PolicyManagementContext [D,CF]<-[U,OHS,PL] CustomerManagementContext {
  implementationTechnology = "RESTful HTTP"
}

Responsibility Layers

职责层可以在有边界的上下文和聚合上使用,以指定它们的职责

语法示例

BoundedContext CustomerManagementContext implements CustomerManagementDomain {
  type = FEATURE
  domainVisionStatement = "The customer management context is responsible for ..."
  implementationTechnology = "Java, JEE Application"
  responsibilities = "Customers", "Addresses"
}
Aggregate Customers {
  responsibilities = "Customers", "Addresses"
  
  Entity Customer { 
    aggregateRoot
    
    - SocialInsuranceNumber sin
    String firstname
    String lastname
    - List<Address> addresses
  }
}

Aggregate

聚合在DDD建模过程中是一个很重要的概念,ContextMapper 会有一个全新的规范和校验语法,详细可参考 http://sculptorgenerator.org/documentation/advanced-tutorial

等后续有时间我们再维护到此文档上来。此版本我们只说明一般使用方式达到我们能够科学合理建模即可。

聚合支持职责模式。 聚合可以进一步包含服务、资源、消费者和SimpleDomainObjects(实体、值对象、领域事件等),这些在这里没有进一步介绍。 如前所述,请参考单独的文档说明。

语法示例

Aggregate Contract {
  responsibilities = "Contracts", "Policies"

  Entity Contract {
    aggregateRoot

    - ContractId identifier
    - Customer client
    - List<Product> products
  }

  enum States {
    aggregateLifecycle
    CREATED, POLICE_CREATED, RECALLED
  }

  ValueObject ContractId {
    int contractId key
  }

  Entity Policy {
    int policyNr
    - Contract contract
    BigDecimal price
  }

  Service ContractService {
    @ContractId createContract(@Contract contrace) : write [ -> CREATED];
    @Contract getContract(@ContractId contractId) : read-only;
    boolean createPolicy(@ContractId contractId) : write [ CREATED -> POLICE_CREATED ];
    boolean recall(@ContractId contractId) : write [ CREATED, POLICE_CREATED -> RECALLED ];
  }
}
展开阅读全文
加载中

作者的其它热门文章

打赏
0
0 收藏
分享
打赏
0 评论
0 收藏
0
分享
返回顶部
顶部