zoukankan      html  css  js  c++  java
  • 领域驱动系列(3)--模型的设计

    一、概念

    领域模型并不能直接带来收益,只是辅助我们去理解正在做的事情。

    引用百度的说法,“领域模型是对领域内的概念类或现实世界中对象的可视化表示。又称概念模型、领域对象模型、分析对象模型。它专注于分析问题领域本身,发掘重要的业务领域概念,并建立业务领域概念之间的关系。”总结一下,就是“准确描述问题,清晰描述方案”。

                 

    如果说软件开发的本质,是从“问题空间”到“解决方案空间”的转化,那么“领域模型”,就是“解决方案空间”的架构,通过抽象的模型,为系统带来统一的认知。

    实体(Entity) & 值对象(Value Object)

    实体与面向对象中的概念类似,在这里再次提出是因为它是领域模型的基本元素。在领域模型中,实体应该具有唯一的标识符,从设计的一开始就应该考虑实体,决定是否建立一个实体也是十分重要的。

    值对象和我们说的编程中数值类型的变量是不同的,它仅仅是没有唯一标识符的实体,比如有两个收获地址的信息完全一样,那它就是值对象,并不是实体。值对象在领域模型中是可以被共享的,他们应该是“不可变的”(只读的),当有其他地方需要用到值对象时,可以将它的副本作为参数传递。

    服务(Services)

    当我们在分析某一领域时,一直在尝试如何将信息转化为领域模型,但并非所有的点我们都能用Model来涵盖。对象应当有属性,状态和行为,但有时领域中有一些行为是无法映射到具体的对象中的,我们也不能强行将其放入在某一个模型对象中,而将其单独作为一个方法又没有地方,此时就需要服务.

    服务具有以下特点:

    a)服务中体现的行为一定是不属于任何实体和值对象的,但它属于领域模型的范围内
    b)服务的行为一定设计其他多个对象
    c)服务的操作是无状态的

    模块(Moudles)

    对于一个复杂的应用来说,领域模型将会变的越来越大,以至于很难去描述和理解,更别提模型之间的关系了。模块的出现,就是为了组织统一的模型概念来达到减少复杂性的目的的。而另一个原因则是模块可以提高代码质量和可维护性,比如我们常说的高内聚,低耦合就是要提倡将相关的类内聚在一起实现模块化。

    聚合(Aggregates)

    聚合被看作是多个模型单元间的组合,它定义了模型的关系和边界。每个聚合都有一个根,根是一个实体,并且是唯一可被外访问的。正是如此,聚合可以保证多个模型单元的不变性,因为其他模型都参考聚合的根。所以要想改变其他对象,只能通过聚合的根去操作。根如果没有了,那么聚合中的其他对象也将不存在。

    工厂(Factories)

    在大型系统中,实体和聚合通常是很复杂的,这就导致了很难去通过构造器来创建对象。工厂就决解了这个问题,它把创建对象的细节封装起来,巧妙的实现了依赖反转。当然对聚合也适用(当建立了聚合根时,其他对象可以自动创建)。工厂最早被大家熟知可能还是在设计模式中,的确,在这里提到的工厂也是这个概念。

    但是不要盲目的去应用工厂,以下场景不需要工厂:
    a)构造器很简单
    b)构造对象时不依赖于其他对象的创建
    c)用策略模式就可以解决

    仓库(Repository)

    仓库封装了获取对象的逻辑,领域对象无须和底层数据库交互,它只需要从仓库中获取对象即可。仓库可以存储对象的引用,当一个对象被创建后,它可能会被存储到仓库中,那么下次就可以从仓库取。如果用户请求的数据没在仓库中,则会从数据库里取,这就减少了底层交互的次数。

     

    二、模型设计

    设计领域模型之前,首先要确定“问题空间”,即对需求进行分析和拆解。值得注意的是,领域模型通常是针对比较大的系统设计而言,如果是日常功能迭代中的小需求,那么只需要根据已经设计好的模型原则,来做对应的开发即可。

    需求分析阶段要做的就是确定系统要实现的核心功能是什么,用UML来表达设计意图是非常好的工具。UML通过动静结合的图示,便可以比较清楚的阐述系统的核心职责与过程。

    类图:静态,展示了模型中存在的类、类的内部结构以及它们与其他类的关系等;

    状态机:动态,对一个单独对象的行为建模,指明对象在它的整个生命周期里,响应不同事件时,执行相关事件的顺序;

    时序图:动态,描述对象之间发送消息的时间顺序显示多个对象之间的动态协作。

    接下来,就是业务建模阶段了。在大多数讲领域驱动的书籍里,都会将领域划分为:领域,子域和限界上下文。领域是一个模型所包含的全部事情;子域是指模型的某个细分部分,根据重要性可以划分为核心、支撑和通用;限界上下文是一个业务边界,边界内的术语都有其特定的涵义,例如银行系统和电商系统中顾客就不是同一个意思。这种划分方式通常是非常灵活的,跟设计者的个人经验有比较大的关系。

    在DDD中通常可以分为四个层次:用户界面、应用层、领域层、基础设施层。

                                                                                   层结构(Layered Architecture)

    1)、User Interface: 负责向用户展现信息,并且会解析用户行为,也就是常说的展现层。

    2)、Application Layer :应用层没有任何的业务逻辑代码,它很简单,它主要为程序提供任务处理。

    3)、Domain Layer: 这一层包含有关领域的信息,是业务的核心,领域模型的状态都直接或间接(持久化至数据库)存储在这一层。

    4)、Infrastructure Layer:为其他层提供底层依赖操作。

    层结构的划分是很有必要的,只有清晰的结构,那么最终的领域设计才宜用。

             

                                                                                     结构层次模型

    用户界面,也就是表示层。为用户提供操作界面,同时也会做一些简单的逻辑运算。这个层次一般可以采用MVC风格进行细化,View(V)提供操作界面、Controller(C)作为调度器接收用户请求并选择合适的View显示给用户、Modle(M)在这里只作为表现层简单的数据模型,而不是整个系统的业务模型。它通常可以是封装了一定属性的视图对象(VO/DTO),由应用层提供。

    应用层,它和领域层一起作为整个系统的业务逻辑层。业务逻辑主要是放在领域层,而应用层只是一层比较薄的封装。它的主要职责是封装业务逻辑的具体实现、为表现层提供统一的访问入口(说白了就是Facade)。它的工作流程是这样的:接受表现层的请求(Request)、调度领域层完成任务、最后将处理结果转换为DTO(Response)返回给表现层。事务的管理一般也放在这一层。

    领域层,是整个系统的核心部分,大部分的业务逻辑都在这一层实现。领域层细分为几个部分:领域模型、服务、工厂、仓储。领域模型(DomainModle)是其中最重要的概念,它是从领域专家提供的业务知识中直接提炼出来的,表现了业务知识以及它们之间关系。对于一些业务适合放在领域模型中(比如管理领域模型),我们将其抽取出来做成服务(Service),它也是业务规则的一部分。由于每套领域模型都具备一定的复杂结构(聚集),所以可以通过工厂(Factory)来统一生产领域模型。而仓储(Reposistory)则专门负责持久化操作,以使得业务层可以专心于业务,不用管持久化的细节。

    基础设施层,这一层已经和业务逻辑没有关系了,它封装了数据库操作、文件IO、远程通信等计算机基础操作。

    将系统拆分完成后,就需要确定子域/上下文之间的关系,以及相互之间的信息如何进行流转,例如:

    模型概念

    模型属性和属性值

    模型之间的关系

                                                                                      模型关系图(Model-Driven Design)

     

     

                                                                               简单工厂结构图

    三、总结

    最后,就是通过某些具体的方法论,分析具体的模型设计了。设计的方法论也有很多种,例如在用例分析方法中,就是尽可能的收集用例素材,通过例图,以图形化的方式将系统描述成用例、参与者(用户)及其之间的关系;在DODAF中,是采用标准方法,表述“EA的数据和关系类型”的指引,解决复杂系统结构化问题的指引。在四色建模法中,用蓝色表示命令,用红色表示实体,用绿色表示领域事件,用黄色表示补充信息,相关的实体便可以整理到同一个领域中。建模方式有很多种,不论是已有的方法论,或者是通过日常的业务经验积累来设计,只要能将模型描述清楚,都是可用的。

    做个总结,就是抛开技术视角,用纯业务的视角来建立模型。但确定的一点是,领域模型的优先级要高于软件解决方案,先有领域建模的整体框架,然后才是将模型映射到软件架构之中。

    参考:

    http://www.uml.org.cn/sjms/202010092.asp

    https://blog.csdn.net/kkkkkxiaofei/article/details/62237121

    https://blog.csdn.net/asukamk2/article/details/3255563

    https://blog.csdn.net/mengdong_zy/article/details/8802485

  • 相关阅读:
    Lua ip转整数
    纯lua实现Base64加密与解密
    lua之base64的解码和编码(三种方案实现)
    Lua 5.1 位操作(与,或,异或操作)
    Lua打印Table对象
    Lua 截取字符串(截取utf-8格式字符串)
    lua 截取字符,以及取字符个数(非字符串长度)
    lua 加密解密
    Openwrt与贝壳物联平台通讯示例
    php socket编程:使用socket_recv而不是socket_read
  • 原文地址:https://www.cnblogs.com/sfnz/p/14171176.html
Copyright © 2011-2022 走看看