zoukankan      html  css  js  c++  java
  • DDD—Repository仓储&工厂模式

      一、解耦领域层和基础层       
              DDD严格的分层架构告诉我们,每一层只能与其下方的一层发生耦合。因此用户接口层只与应用层发生交互,应用层往下只与领域层发生交互,领域层往下只与基础层发生交互。
        在传统的代码分层结构Controller—Service—Dao结构中,经常能看到在Service业务实现层的代码中嵌入SQL,或者在其中频繁出现修改数据对象并调用DAO的情况。这样,基础层的数据处理逻辑就渗透到了业务逻辑代码中。
        在DDD的分层结构中,如果出现上述情况,则基础层的数据处理逻辑就渗透到了领域层,领域层中的领域模型就难以聚焦在业务逻辑上,对外层的基础层产生了依赖。而一旦涉及到数据逻辑的修改,就要到领域层中去修改代码,重新调试领域层与基础层的交互,或者当切换异构数据库类型时,需要大量修改领域层的代码,将业务逻辑和数据处理逻辑重新适配(主要在于异构数据库导致的SQL或数据对象调整),因此技术升级会变得特别麻烦。
        本文要讲的仓储模式就是用来解耦领域层和基础层的,降低他们之间的耦合和相互影响
        
     
      二、仓储模式
           为了解耦领域逻辑和数据处理逻辑,在中间加了薄薄的一层仓储。
        仓储模式包含仓储接口和仓储实现,仓储接口面向领域层提供基础层数据处理相关的接口,仓储实现则完成仓储接口对应的数据持久化相关的逻辑处理。一个聚合配备一个仓储,由仓储完成聚合数据的持久化。领域层逻辑面向仓储接口编程,聚合内的数据持久化过程为DO(领域对象)转PO(持久化对象)。
        当需要更换数据库类型,或者更改数据处理逻辑时,我们就可以保持业务逻辑接口不动,只修改仓储实现,保证了领域层业务逻辑的干净和纯洁。
        如下示例为一个人员聚合中对人员实体的仓储模式实现:
        人员DO和人员PO定义:  
    /**
     * 人员聚合
     * @author test11
     */
    public class Person {
    
        //人员id
        private String id;
    
        //姓名
        private String name;
    
        //地址(值对象)
        private Address address;
    
        //上班行为
        private void goWork(){
    
        }
    
        //下班行为
        private void leaveWork(){
    
        }
    
        public String getId() {
            return id;
        }
    
        public void setId(String id) {
            this.id = id;
        }
    
        public String getName() {
            return name;
        }
    
        public void setName(String name) {
            this.name = name;
        }
    
        public Address getAddress() {
            return address;
        }
    
        public void setAddress(Address address) {
            this.address = address;
        }
        
    }
    /**
     * 人员聚合的持久化PO
     * @author test11
     */
    public class PersonPO {
    
        //人员id
        private String id;
    
        //姓名
        private String name;
    
        //地址
        private Address address;
    
        public String getId() {
            return id;
        }
    
        public void setId(String id) {
            this.id = id;
        }
    
        public String getName() {
            return name;
        }
    
        public void setName(String name) {
            this.name = name;
        }
    
        public Address getAddress() {
            return address;
        }
    
        public void setAddress(Address address) {
            this.address = address;
        }
    }
    /**
     * 地址值对象
     */
    public class Address {
    
        //省份
        private String province;
        //城市
        private String city;
        //街道
        private String street;
    }
        仓储接口定义:
    /**
     * 人员聚合仓储接口
     * @author test11
     */
    public interface PersonRepository {
    
        /**
         * 添加人员
         */
        void addPerson(PersonPO personPO);
    
        /**
         * 更新人员
         */
        void updatePerson(PersonPO personPO);
    
        /**
         * 根据id查找人员PO对象
         * @return
         */
        PersonPO findById(String id);
    }

        

        仓储接口实现:

    /**
     * 人员仓储实现
     * @author test11
     */
    public class PersonRepositoryImpl implements PersonRepository{
    
        @Resource
        PersonDao personDao;
    
        @Override
        public void addPerson(PersonPO personPO) {
            personDao.addPerson(personPO);
        }
    
        @Override
        public void updatePerson(PersonPO personPO) {
            personDao.updatePerson(personPO);
        }
    
        @Override
        public PersonPO findById(String id) {
            return personDao.findById(id);
        }
    }

        

        人员领域服务实现:后面基础层发生了变化,则领域层无需动任何代码,只要仓储接口不变,领域层的逻辑就可以一直保持不变,维护了领域层的稳定性。领域服务是可以做成企业级可复用的服务的,因此稳定性必须有保障。

    import javax.annotation.Resource;
    
    /**
     * 人员领域服务聚合类
     * @author test11
     */
    public class PersonDomainService {
    
        @Resource
        PersonRepository personRepository;
    
        public void addPerson(PersonPO personPO) {
            personRepository.addPerson(personPO);
        }
    }
     
      三、工厂模式

      DO对象创建时,需要确保聚合根和它依赖的对象同时被创建,如果这项工作交给聚合根来实现,则聚合根的构造函数将变得异常庞大,所以我们把通用的初始化DO的逻辑,放到工厂中去实现,通过工厂模式封装聚合内复杂对象的创建过程,完成聚合根,实体和值对象的创建。DO对象创建时,通过仓储从数据库中获取PO对象,通过工厂完成PO到DO的转换

      工厂中还可以包含DO到PO对象的转换过程,方便完成数据的持久化。

    /**
     * Person聚合的工厂
     * DO和PO的转换
     * @author test11
     */
    public class PersonFactory {
    
        /**
         * 人员PO到领域对象的数据初始化
         * @param personPO
         * @return
         */
        protected Person createPerson(PersonPO personPO){
            Person person = new Person();
            person.setId(personPO.getId());
            person.setName(personPO.getName());
            person.setAddress(personPO.getAddress());
            return person;
        }
    
        /**
         * 领域对象到持久化对象PO的转换
         * @param person
         * @return
         */
        protected PersonPO createPersonPO(Person person){
            PersonPO personPO = new PersonPO();
            personPO.setId(person.getId());
            personPO.setName(person.getName());
            personPO.setAddress(person.getAddress());
            return personPO;
        }
        
    }

      

      参考书籍 ——《基于DDD和微服务的中台架构与实现》欧创新、邓頔
      参考书籍 ——《领域驱动设计》Eric Evans
      参考书籍 ——《架构真经》Martin L. Abbott

  • 相关阅读:
    解决PowerDesigner中DBMS设置的问题-创建物理模型时DBMS选项为空
    转载 缓存技术Redis在C#中的使用及Redis的封装
    T4模版 mysql
    T4 模版应用类
    在点击run之后,没有显示任何设备,这一般是adb的问题,解决方法是重新启动adb
    webapi 返回不同格式的数据
    使用elk+redis搭建nginx日志分析平台
    [C#]使用WebClient上传文件并同时Post表单数据字段到服务端
    关于AJAX跨域调用ASP.NET MVC或者WebAPI服务的问题及解决方案
    2019.12.09-新闻列表布局代码
  • 原文地址:https://www.cnblogs.com/jiyukai/p/14825980.html
Copyright © 2011-2022 走看看