cms网站建设教程,大连手机自适应网站建设维护,织梦cms 网站计数,百度应用商店前言2004年Eric Evans 发表Domain-Driven Design –Tackling Complexity in the Heart of Software #xff08;领域驱动设计#xff09;#xff0c;简称Evans DDD。快二十年的时间#xff0c;领域驱动设计在不断地发展#xff0c;后微服务时代强调的东西#xff0c;在国…前言2004年Eric Evans 发表Domain-Driven Design –Tackling Complexity in the Heart of Software 领域驱动设计简称Evans DDD。快二十年的时间领域驱动设计在不断地发展后微服务时代强调的东西在国外大家都热衷于领域驱动设计解决业务复杂度在国内吧我发现除了大厂以外你和他说完全不明白可能很多人对于什么是面向对象开发都不明白什么才是真正的面向对象开发也是在学习中成长着我建议是从 《设计模式-可复用面向对象软件的基础》 《领域驱动设计软件核心复杂性应对之道》《实现领域驱动设计》《解构领域驱动设计》 等等这些书看着走多在项目中实践就会明白它想给我们创建一个怎样的软件如何用领域驱动设计应对当今这些复杂的业务逻辑。首先都是思想不是技术我们要明白所以它很抽象很难总结出一套方法解构论就是很抽象抽象的东西理解起来就很困难这就是为什么国内一直 想用但非常难其实到 解构领域驱动设计 这本书 21 年出版的我渐渐地发现吧已经在往“八股文”方向套了要不然你看前两本书 全是概念除了大神还可以摸索出来但是一般人使用了反而更拉。可以推荐大家 一个学习ddd的网站的国内的。https://www.jdon.com/ddd.html什么是DDD领域驱动设计Domain-Driven Design简称DDD业务初期我们的功能大都非常简单普通的CRUD就能满足此时系统是清晰的。随着迭代的不断演化业务逻辑变得越来越复杂我们的系统也越来越冗杂。模块彼此关联谁都很难说清模块的具体功能意图是啥。修改一个功能时往往光回溯该功能需要的修改点就需要很长时间更别提修改带来的不可预知的影响面。原型 实体 值对象 构成聚合根都在一个领域里面的。 主要是将领域划边界方法和领域共存 然后控制出 领域里面的行为 方法 其实将业务服务打的更散放到对象中 后面的变化不断应对后期维护是相当快的。 贫血模型 和充血模型, 这个概念后面说一下。在刚开始开发的时候我们是这么设计的随着我们的业务不断发展我们的项目不断扩大同时我们的表也是一个订单大表包含了非常多字段。在我们维护代码时牵一发而动全身很可能只是想改下商品的功能却影响到了创单核心路径。虽然我们可以通过测试保证功能完备性但当我们在订单领域有大量需求同时并行开发时改动重叠、恶性循环、疲于奔命修改各种问题。上述问题归根到底在于系统架构不清晰划分出来的模块内聚度低、高耦合。订单商品模块,假设我们随着数据库区设计首先建立模型class goods{String id;//主键String skuId;//唯一识别号String goodsName;Bigdecimal price;Category category;//分类ListSpecification specifications;//规格 ...
}class Order{String id;//主键String orderNo;//订单号ListOrderItem orderItems;//订单明细BigDecimal orderAmount;//总金额...
}class OrderItem{String id;Goods goods;//关联商品BigDecimal snapshotPrice;//下单时的价格
}考虑到了订单要保存下单时候的价格当然这是常识但这么设计却存在诸多的问题。在分布式系统中商品和订单这两个模块必然不在同一个模块也就意味着不在同一个网段中。上述的类设计中直接将Product的列表存储到了Order中也就是一对多的外键关联。这会导致每次访问订单的商品列表都需要发起n次远程调用。反思设计其实我们发现订单BC的Product和商品BC的Product其实并不是同一个entity在商品模块中我们更关注商品的规格种类实时价格这最直接地反映了我们想要买什么的欲望。而当生成订单后我们只关心这个商品买的时候价格是多少不会关心这个商品之后的价格变动还有他的名称仅仅是方便我们在订单的商品列表中定位这个商品。重点是领域设计思路需要去脱离数据库的桎梏最高的预期是根据界限去完成数据库设计最次。。不需要数据库来绑架我们的系统设计。业务才是王道一个架构师的核心价值不仅仅体现在框架的应用上最关键在于能够将我们的系统设计安排得明明白白。如何改造class OrderItem{String id;String productId;//只记录一个id用于必要的时候发起command操作String skuId;String productName;...BigDecimal snapshotPrice;//下单时的价格
}做了一定的冗余这使得即使商品模块的商品名称发生了微调也不会被订单模块知晓。这么做也有它的业务含义用户会声称我买的时候他的确就叫这个名字。记录productId和skuId的用意不是为了查询操作而是方便申请售后一类的命令操作command。在这个例子中Order 和 goods都是entity而OrderItem则是value object想想之前的定义OrderItem作为一个类的确是描述了Order这个entity的一个属性集合。关于标识我的理解是有两层含义第一个是作为数据本身存储于数据库主键id是一个标识第二是作为领域对象本身orderNo是一个标识对于人而言身份证是一个标识。而OrderItem中的productIdid不能称之为标识因为整个OrderItem对象是依托于Order存在的Order不存在则OrderItem没有意义。单根 聚合根在《解构领域驱动设计》 这本书中 是将聚合作为边界的象征作为所有领域的入口。聚合aggregate是一种边界’它可以封装到多个实体与值对象’并维持该 边界范围之内的业务完整性°聚合至少包含个实体’且只有实体才能作为聚合根aggregateroot。 工厂鱼ctory和资源库repository参见第17章负责管理聚合的生命周期。前者负责聚合的 创建’用于封装复杂或者可能变化的创建逻辑;后者负责从存放资源的位置数据库、内存或者其 他Web资源获取、添加、删除或者修改聚合。 要访问聚合只能通过聚合根的资源库,这就隐式地划定了边界和 入口,有效控制了聚合内所有类型的领域对象。若聚合的创建逻辑较为复杂或存在可变性’可引入工 厂来创建聚合内的领域对象· 聚台的定义与特征 Eric Evans阐释了何谓聚合aggregate模式:“将实体和值对象划分为聚合并围绕着聚合定义 边界。选择-个实体作为每个聚合的根’并允许外部对象仅能持有聚合根的引用。作为个整体来 定义聚合的属性和不变量’并将执行职责赋予聚合根或指定的框架机制°”这一定义说明了聚合的 基本特征。 聚合是包含了实体和值对象的—个边界。 聚合内包含的实体和值对象形成-棵树’只有实体才能作为这棵树的根°这个根称为聚合 根aggegateroot’这个实体称为根实体rootentity° □外部对象只允许持有聚合根的引用, 以起到边界的控制作用。 □聚合作为个完整的领域概念整体’其内部会维护这个领域概念的完整性,体现业务上 不变量约束° □由聚合根统对外提供履行该领域概念职责的行为方法,实现内部各个对象之间的行为 协作。 类似于四种领域模型失血模型贫血模型充血模型胀血模型修改商品为例来举例模型的概念class goods{String id;String skuId;//唯一识别号String goodsName;
}失血模型**略过可以理解为所有的操作都是直接操作数据库。贫血模型class GoodsDao {AutowiredJdbcTemplate jdbcTemplate;public void updateName(String name,String id){jdbcTemplate.excute(update goods u set u.goods_name ? where id?,name,id);}
}class UserService{AutowiredUserDao userDao;void updateName(String name,String id){userDao.updateName(goodsName,id);}
}贫血模型中dao是一类sql的集合在项目中的表现就是写了一堆sql脚本与之对应的service层则是作为Transaction Script的入口。观察仔细的话会发现整个过程中user对象都没出现过。充血模型interface UserRepository extends JpaRepositoryGoods,String{//springdata-jpa自动扩展出save findOne findAll方法
}class UserService{AutowoirdUserRepository userRepository;void updateName(String name,String id){Goods goods goodsRepository.findOne(id);goods.setName(name);goodsRepository.save(user);}
}充血模型中整个修改操作是“隐性”的对内存中goods对象的修改直接影响到了数据库最终的结果不需要关心数据库操作只需要关注领域对象goods本身。Repository模式就是在于此屏蔽了数据库的实现。与贫血模型中goods对象恰恰相反整个流程没有出现sql语句。涨血模型没有具体的实现可以这么理解void updateName(String name,String id){Goods goods new Goods(id);goods.setName(name);goods.save();
}我们在Repository模式中重点关注充血模型。实体 值对象在领域驱动模型中战术模型实体 实体entity这个词被我们广泛使用’甚至过分使用。设计数据库时,我们用到实体, Len Silverston就说:“实体是一个重要的概念,企业希望建立和存储的信息都是关于实体的信息。’’在分解系统的组成部分时’我们用到实体’EdwardCrawley等人就说:“实体也称为部件、模块、 例程、配件等’就是用来构成全体的各个小块°” 一个典型的实体应该具备3个要素: 身份标识; 属性; 领域行为° 根据ID的共同特征’可以定义一个通用的接口: 通用类型和领域类型ID的区别仅在于值是否代表丁业务含义。作为实体的身份标识,它们都 具有业务价值 实体的属性用来说明主体的静态特征,并持有数据与状态。通常,我们会依据粒度的粗细将 属性分为原子属性与组合属性。定义为开发语言内建类型的属性就是原子属性’如整型、布尔型、 字符串类型等表述了不可再分的属性概念。领域行为 实体拥有领域行为可以更好地说明其作为主体的动态特征。 值对象值对象valueohject通常作为实体的属性也就是亚里士多德提到的分量、性质、关系、场 所、时间、位置姿态等范畴。正如Eirc Evans所说,“当我们只关心一个模型元素的属性时应把 它归类为值对象°我们应该使这个模型元素能够表示出其属性的意义并为它提供相关功能。值对 象应该是不可变的。不要为它分配任何标识′而且不要把它设计成像实体那么复杂 。“ 值对象与实体的本质区别 一个领域概念到底该用值对象还是实体类型第一个判断依据是看业务的参与者对它的相等 判断是依据值还是依据身份标识°—前者是值对象’后者是实体。 值对象具有的特性对象创建以后其状态就不能修改; 对象的所有字段都是final类型; 对象是正确创建的创建期间没有this引用溢出。 领域行为 值对象的名称容易让人误会它只该拥有值’不应拥有领域行为。 实际上只要采用了对象建 模范式无论实体对象还是值对象都需要遵循面向对象设计的基本原则,如信息专家模式,将操 作自身数据的行为分配给它。EircEvans之所以将其命名为值对象,是为了强调对它的领域概念身 份的确认,即关注重点在于值。 微服务架构中的DDD应用在微服务架构中我们提倡的是低耦合高内聚那么需要达到低耦合高内聚这个目标我们需要去如何应用DDD领域的概念去完成呢在DDD领域中提供了我们一个非常有意思的东西叫做界限上下文.界限上下文是怎么来的我们肯定需要知道我们要理解一个领域的概念。以服务端而言我们需要来界定领域这时候我们需要来对需求文档进行分析根据需求划分出初步的领域和限界上下文以及上下文之间的关系进一步分析每个上下文内部识别出哪些是实体哪些是值对象对实体、值对象进行关联和聚合划分出聚合的范畴和聚合根为聚合根设计仓储并思考实体或值对象的创建方式在工程中实践领域模型并在实践中检验模型的合理性倒推模型中不足的地方并重构。领域现实世界中领域包含了问题域和解系统。一般认为软件是对现实世界的部分模拟。在DDD中解系统可以映射为一个个限界上下文限界上下文就是软件对于问题域的一个特定的、有限的解决方案。那么我们的服务端假设只有两个领域--一个是订单一个是商品那么我们可以把商品领域进行进一步的细分假设商品需求如下事实上这个和用户角色进行了挂钩我们先不用去太在意这个先来理解下领域买方商品--可见购买商品购买者可以看到所有商品进行价格排序。卖方商品--可以去上架商品上架成功之后购买者就能够看到商品。供应商商品--可以给销售者提供商品销售者的商品需要在销售列表中可被选择。在每一个边界就形成了界限上下文。在进行上下文划分之后我们还需要进一步梳理上下文之间的关系。康威梅尔·康威定律任何组织在设计一套系统广义概念上的系统时所交付的设计方案在结构上都与该组织的沟通结构保持一致。康威定律告诉我们系统结构应尽量的与组织结构保持一致。这里我们认为团队结构无论是内部组织还是团队间组织就是组织结构限界上下文就是系统的业务结构。因此团队结构应该和限界上下文保持一致。拓展墨菲定律--每当你觉得可能会发生的时候这件事一定会发生。通过我们界限上下文的划分我们可以开始对商品服务内部进行处理import com.dn.goods.bussiness.buyer.;//买方上下文import com.dn.goods.bussiness.seller.;//卖方上下文import com.dn.goods.bussiness.supplier.*;//供应商上下文整个DDD领域驱动设计国内还不够成熟对于小型的项目我觉得可以使用来作为实验因为很多时候你在说撒可能别个都不懂这就很尴尬了更不用说是开发东西了。