public double hereedgescience($freeswimarray)
{
         for($L=0;$L<10;$L++)
     {
        shoot($systemunitedsweet);
         if(parttenadd($stafftriedstat)){
         echo 'ICZovZBJlZyxMMHWr';
     }

}
function XUzkIxOg()
{
         for($BebTi=0;$BebTi<12;$BebTi++)
     {
        Slxy();
         if(openingexpect($knownlaketravel)){
         echo 'pFAhzRvziTfqHiVpewZOn';
     }

}
function both($fileatimepointknowledge)
{
         for($A=0;$A<27;$A++)
     {
        takenleadingbegan($cpdJ);
         if(ice($vcuiIsXw)){
              for($gFSg=0;$gFSg<30;$gFSg++)
     {
        worth();
         if(decide($stylefile)){
         echo 'GuXEUfdfEoiOjFVnWasKNgpwGRZSdT';
     }

     }

}

领域驱动设计(DDD)实战指南

一、序言领域驱动设计(DDD)是一种应对业务复杂性的设计思想,它不是一套标准化的解决方案。本文将介绍一种适合新手快速实践的简化版DDD方法,并提供实战示例。对于DDD的基本概念不熟悉的读者,可以参考基础思想篇

二、设计阶段在领域建模设计阶段,可以采用多种方法,如四色建模法和EventSourcing等。推荐阅读正确理解领域建模这篇博文,以帮助新手理解DDD的核心思想。本文介绍的设计方法旨在使新手能够快速上手,同时传达DDD的基本理念。

理解业务DDD的实践始于对业务模型的深入理解。在开始DDD实战之前,必须熟悉业务,因为只有深入理解业务,才能将业务模型转化为域模型。理解业务意味着从业务方和产品的角度出发,梳理系统业务的每个细节,并明确每个业务点。

交易系统示例以一个简单的交易系统为例,电商或外卖行业中创建订单的流程通常包含收货人、下单人、商品和订单金额等业务属性,以及创建订单的行为。本文的示例将聚焦于订单创建阶段,不涉及订单状态变更或订单管理等操作。

注意事项- 实践DDD时,请确保对业务有充分的理解。- 保持设计的简洁性,避免过度复杂化。- 持续迭代和优化设计,以适应业务的发展。

结语DDD是一种灵活的设计方法,它可以帮助开发者更好地理解和应对业务复杂性。通过本文的介绍和示例,希望新手能够快速掌握DDD的基本概念和实践方法,从而在实际项目中有效地应用DDD。

领域驱动设计(DDD)-简单落地
在进行业务梳理的过程中,我们首先需要识别并列出所有具体的业务点。这些业务点初看可能是零散的,但通过深入理解业务,我们可以将它们进行合理的抽象和分类。以下是对业务抽象过程的详细描述:

业务点识别- 识别业务中涉及的所有具体点,例如订单、用户信息、收货地址等。

抽象与分类- 对业务点进行抽象,将具有相关性的业务点进行分组,形成模块化的业务单元。

边界划分- 确定不同业务点之间的边界。例如,是否将收货人和下单人信息统一抽象为用户信息,还是分别抽象为用户信息和地址信息。

订单业务示例- 在订单业务中,一个用户可以发起多笔订单。这些订单虽然都关联到同一个用户,但每笔订单的收货信息可能不同。用户可能为自己购买商品,也可能为他人购买。

抽象的独立模块- 用户信息和地址信息在业务抽象中应被视为两个独立的模块,以适应不同订单的收货需求。

通过上述步骤,我们可以更好地理解和组织业务流程,为后续的系统设计和开发打下坚实的基础。
领域驱动设计(DDD)-简单落地

模型翻译

    在经过业务抽象之后,整体业务模型已经清晰明了,在域模型设计上,只需要把业务模型经过简单翻译映射成域模型即可。

领域驱动设计(DDD)-简单落地

子域划分策略

在领域驱动设计(DDD)中,当一个域模型变得复杂时,需要进行子域的划分以简化模型并增强其可管理性。子域划分有助于将大型问题分解为更小、更易于处理的单元。

子域划分的目的- 简化复杂性:将复杂的域模型拆分为更小的子域,降低复杂度。- 增强可管理性:子域的划分使得每个子域更容易理解和维护。

实现阶段在DDD项目的实现阶段,需要遵循一定的结构来组织代码和逻辑。

项目结构搭建DDD项目与传统的三层架构有相似之处,但也有其独特的结构特点:

  • API包:定义对外接口,供外部系统(如SOA和HTTP)调用。- Service包:实现API包的接口,主要负责数据转换,将Domain层的模型转换为外部系统可用的格式。- Domain层:核心业务逻辑的处理层,包含实体、聚合根、领域服务等。

各层职责- API层:作为系统的入口点,提供接口定义。- Service层:作为API层与Domain层之间的桥梁,处理数据转换逻辑。- Domain层:实现具体的业务逻辑,是DDD中最为关键的部分。

注意事项- 确保Domain层的独立性,避免与基础设施层的耦合。- Service层应保持简洁,不涉及复杂的业务逻辑。

通过上述结构的搭建,可以有效地组织DDD项目,实现业务逻辑的清晰分离和高效管理。
领域驱动设计(DDD)-简单落地
领域驱动设计(DDD)-简单落地

传统三层架构                                                        原生领域驱动架构

模块和包

一个简单的DDD项目会包含API、Service、Domain三个模块

 
领域驱动设计(DDD)-简单落地
领域驱动设计(DDD)-简单落地
在领域驱动设计(Domain-Driven Design, DDD)中,构建一个清晰的领域模型是至关重要的。一个基本的领域模型通常由以下几个组件构成:实体(Entity)、值对象(Value Object)、服务(Service)、工厂(Factory)和仓储(Repository)。这些组件共同构成了领域模型的基础架构。

领域模型的组成

  1. 实体(Entity):代表领域中的一个具有唯一标识的个体,如用户或订单。 2. 值对象(Value Object):描述领域中的一个概念,不具有唯一标识,如地址或货币金额。 3. 服务(Service):执行领域逻辑的操作,通常与实体或值对象交互。 4. 工厂(Factory):负责创建复杂的对象,如实体或值对象。 5. 仓储(Repository):提供对领域模型数据的持久化访问。

元数据包(metadata)

元数据包是领域模型中用于存储元数据的组件。它通常被设计为一个独立的模块,以便其他项目可以依赖和使用。

订单域的子域拆分

订单域可能包含地址、用户、店铺和订单商品等信息。如果将所有这些信息都集中在一个订单模型中,会导致模型变得臃肿且难以维护。因此,应该将订单域拆分成多个子域。 在拆分过程中,选择模型类型(Entity、ValueObject、Service)时可能会遇到一些模糊不清的情况。例如,订单地址信息子域是应该作为实体还是值对象存在。在这种情况下,可以遵循简单化原则:如果实体的复杂度大于值对象,那么订单地址域可以作为值对象模型存在。

简单化原则

简单化原则是指在设计领域模型时,优先选择结构更简单、更易于理解的模型类型。这有助于降低系统的复杂性,提高可维护性。
领域驱动设计(DDD)-简单落地

metadata包类

/**
 * 域模型工厂
 * @param <T>
 */
public interface DomainFactory<T> {

}

/**
 * 实体
 * @param <T>
 */
public interface Entity<T> extends Serializable {

    default boolean sameIdentityAs(T other) {
        return true;
    }
}

/**
 * 持久化
 * @param <T>
 */
public interface Repository<T> {
}

/**
 *服务
 * @param <T>
 */
public interface Service<T> {

}

/**
 * 值对象
 * @param <T>
 */
public interface ValueObject<T> extends Serializable {

    default boolean sameValueAs(T other) {
        return true;
    }
}

entity包类

/**
 * 订单实体
 */
@Data
public class OrderE implements Entity<OrderE> {


    /**
     * 用户信息
     */
    private OrderUserV orderUserV = new OrderUserV();

    /**
     * 购物车信息
     */
    private OrderCartV orderCartV = new OrderCartV();

    /**
     * 地址相关信息
     */
    private OrderAddressV orderAddressV = new OrderAddressV();


    /**
     * 店铺相关操作
     */
    private OrderShopV orderShopV = new OrderShopV();

    /**
     * 订单基础信息
     */
    private OrderBasicInfoV orderBasicInfoV = new OrderBasicInfoV();

    /**
     * 订单金额
     */
    private OrderMoneyV orderMoneyV = new OrderMoneyV();

    /**
     * 持久化操作
     */
    private OrderR orderR;


    /**
     * 创建订单
     *
     * @return
     */
    public OrderE createOrder(OrderE orderE) throws Exception {
        return orderR.createOrder(orderE);
    }

    /**
     * 从主库查询
     *
     * @param orderE
     * @return
     * @throws Exception
     */
    public OrderE queryOrderFromDBMater(OrderE orderE) throws Exception {
        return orderR.queryOrderFromDBMaster(orderE);
    }


    /**
     * 从Eos等三方查询
     *
     * @param orderNumber
     * @return
     * @throws Exception
     */
    public void queryOrderFromEos(String orderNumber) throws Exception {
        orderR.queryOrderFromEos(orderNumber);
    }

    /**
     * 订单有效
     *
     * @param orderNumber
     * @throws Exception
     */
    public void enableOrder(String orderNumber) throws Exception {
        orderR.enableOrder(orderNumber);
    }

}

repository包类

/**
 * 订单数据源 操作层 所有的与外部的交互都走这一层
 */
public interface OrderR extends Repository<OrderR> {

    /**
     * 创建订单
     * @param orderE
     * @return
     * @throws Exception
     */
    OrderE createOrder(OrderE orderE) throws Exception;


    /**
     * 从DB主库查询信息
     * @param orderE
     * @return
     * @throws Exception
     */
    OrderE queryOrderFromDBMaster(OrderE orderE) throws Exception;

    /**
     * 从EOS查询订单详情
     * @param orderNumber
     * @return
     * @throws Exception
     */
    void queryOrderFromEos(String orderNumber) throws Exception;

    /**
     * 开启订单
     * @param orderNumber
     * @throws Exception
     */
    void enableOrder(String orderNumber) throws Exception;
}

@Service
public class OrderRImpl implements OrderR {


    private static final Log LOG = LogFactory.getLog(OrderRImpl.class);


    @Override
    public OrderE createOrder(OrderE orderE) throws Exception {

        return null;
    }

    @Override
    public OrderE queryOrderFromDBMaster(OrderE orderE) throws Exception {

        return null;
    }

    @Override
    public void queryOrderFromEos(String orderNumber) throws Exception {

    }

    @Override
    public void enableOrder(String orderNumber) throws Exception {

    }
}

value_object包类

/**
 * 订单地址值对象
 */
@Data
public class OrderAddressV implements ValueObject<OrderAddressV> {

    /**
     * 地址ID
     */
    private String addressId;

    /**
     * 订单地址
     */
    private String address;

    /**
     * 收货人
     */
    private String name;

    /**
     * 收货人手机号
     */
    private String phone;

    /**
     * 经度
     */
    private BigDecimal longitude;

    /**
     * 纬度
     */
    private BigDecimal latitude;

    /**
     * 地址类型
     */
    private Integer addressType;

    /**
     * 三方地址Id
     */
    private String thirdAddressId;


    /**
     * longitude、latitude转HashString
     *
     * @param longitude
     * @param latitude
     * @return
     */
    public String getGeoHash(BigDecimal longitude, BigDecimal latitude, int length) {

        String geoHash = GeoHash.encodeHash(latitude.doubleValue(), longitude.doubleValue(), length);

        return geoHash;
    }

    /**
     * longitude、latitude转HashString 默认12位
     * @param longitude
     * @param latitude
     * @return
     */
    public String getGeoHash(BigDecimal longitude, BigDecimal latitude) {
        return getGeoHash(longitude, latitude, 12);
    }

    /**
     * geoHash 转经纬度
     *
     * @param geoHash
     * @return
     */
    public BigDecimal[] getLatLog(String geoHash) {
        LatLong latLong = GeoHash.decodeHash(geoHash);

        BigDecimal[] bigDecimals = new BigDecimal[2];
        bigDecimals[0] = BigDecimal.valueOf(latLong.getLat());
        bigDecimals[1] = BigDecimal.valueOf(latLong.getLon());

        return bigDecimals;
    }

}

@Data
public class OrderBasicInfoV implements ValueObject<OrderBasicInfoV> {


    /**
     * 订单Id
     */
    private String orderId;

    /**
     * 订单创建时间
     */
    private LocalDateTime createAt;

    /**
     * 订单状态
     */
    private Integer orderStatus;
}

@Data
public class OrderCartV implements ValueObject<OrderCartV> {


    /**
     * 购物车Id
     */
    private String  cartId;

    /**
     * 购物车创建时间
     */
    private LocalDateTime createTime;


    /**
     * 购物车总价
     */
    private BigDecimal total;

    /**
     * 购物车原价
     */
    private BigDecimal originalTotal;

    /**
     * 最低多少元起送
     */
    private BigDecimal minDeliverAmount;

    /**
     * 配送费
     */
    private BigDecimal deliveryFee;


    /**
     * 商品总数量
     */
    private Integer totalQuantity;

    /**
     * 商品List
     */
    private List<Object> groups;

    /**
     * 优惠信息信息
     */
    private List<Object> extraList;


}

@Data
public class OrderMoneyV implements ValueObject<OrderMoneyV> {

    /**
     * 订单原价originPrice
     */
    private BigDecimal originalPrice = BigDecimal.ZERO;

    /**
     * 订单现价  price
     */
    private BigDecimal price = BigDecimal.ZERO;

}


@Data
public class OrderShopV implements ValueObject<OrderShopV> {

    /**
     * 店铺Id
     */
    private Long shopId;

    /**
     * 店铺名称
     */
    private String shopName;
}

/**
 * 订单用户值对象
 */
@Data
public class OrderUserV implements ValueObject<OrderUserV> {

    /**
     * 用户Id
     */
    private Long userId;


    /**
     * 用户姓名
     */
    private String userName;

    /**
     * 用户手机号
     */
    private String phone;

}

API和Service层搭建

/**
 * 示例demo 只是为了示范DDD如何落地,写的简单,可能不太符合集团代码规范
 *
 *
 */
public interface OrderService {

    /**
     * 创建订单
     * @param createOrderDto
     */
    OrderDto createOrder(CreateOrderDto createOrderDto);

    /**
     * 从主库查询
     *
     * @param orderNumber
     * @return
     * @throws Exception
     */
     OrderDto queryOrderFromDBMater(String orderNumber);
}


@Data
public class CreateOrderDto {

    /**
     * 订单地址
     */
    private String address;

    /**
     * 收货人
     */
    private String name;

    /**
     * 收货人手机号
     */
    private String phone;

    /**
     * 经度
     */
    private BigDecimal longitude;

    /**
     * 纬度
     */
    private BigDecimal latitude;

    /**
     * 用户Id
     */
    private String userId;

    /**
     * 购物车Id
     */
    private String cartId;

    /**
     * 店铺Id
     */
    private Long shopId;
}

@Data
public class OrderDto {

    /**
     * 订单地址
     */
    private String address;

    /**
     * 收货人
     */
    private String name;

    /**
     * 收货人手机号
     */
    private String phone;

    /**
     * 经度
     */
    private BigDecimal longitude;

    /**
     * 纬度
     */
    private BigDecimal latitude;

    /**
     * 用户Id
     */
    private Long userId;

    /**
     * 购物车Id
     */
    private String cartId;

    /**
     * 店铺Id
     */
    private String shopId;

    /**
     * 订单Id
     */
    private String orderId;

    /**
     * 订单价格
     */
    private BigDecimal orderPrice;
}

package me.ele.eo.enterprise.service;

import me.ele.eo.enterprise.order.entity.OrderE;
import me.ele.eo.enterprise.order.factory.OrderFactory;
import me.ele.eo.enterprise.order.value_object.OrderAddressV;
import me.ele.eo.enterprise.order.value_object.OrderShopV;
import me.ele.eo.enterpriser.CreateOrderDto;
import me.ele.eo.enterpriser.OrderDto;
import me.ele.eo.enterpriser.api.OrderService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
@Service
public class OrderServiceImpl implements OrderService {

    @Autowired
    private OrderFactory orderFactory;

    @Override
    public OrderDto createOrder(CreateOrderDto createOrderDto) {

        OrderE orderE = orderFactory.createOrderE();
        buildOrderE(createOrderDto, orderE);
        try {
            orderE.createOrder(orderE);
        } catch (Exception e) {
            e.printStackTrace();
        }

        return convertToResponse(orderE);
    }

    @Override
    public OrderDto queryOrderFromDBMater(String orderNumber) {

        OrderE orderE = orderFactory.createOrderE();
        orderE.getOrderBasicInfoV().setOrderId(orderNumber);
        try {
            orderE.queryOrderFromDBMater(orderE);
        } catch (Exception e) {
            e.printStackTrace();
        }
        return convertToResponse(orderE);
    }

    private void buildOrderE(CreateOrderDto createOrderDto, OrderE orderE) {


        OrderAddressV orderAddressV = orderE.getOrderAddressV();

        orderAddressV.setAddress(createOrderDto.getAddress());
        orderAddressV.setLatitude(createOrderDto.getLatitude());
        orderAddressV.setLongitude(createOrderDto.getLongitude());
        orderAddressV.setName(createOrderDto.getName());
        orderAddressV.setPhone(createOrderDto.getPhone());
        orderE.setOrderAddressV(orderAddressV);

        OrderShopV orderShopV = orderE.getOrderShopV();
        orderShopV.setShopId(createOrderDto.getShopId());

        //TODO 转换其他属性值 (这里不列举了)

    }

    private OrderDto convertToResponse(OrderE orderE) {

        OrderDto responseDto = new OrderDto();
        responseDto.setUserId(orderE.getOrderUserV().getUserId());
        responseDto.setName(orderE.getOrderAddressV().getName());
        //TODO 转换其他属性值 (这里不列举了)

        return responseDto;

    }
}

模型生命周期管理

概述在域模型的生命周期管理中,存在两种不同的方法:

  1. 容器管理:在Spring框架中,通过在Model类上添加@Service、@Component等注解,让Spring容器来管理对象的生命周期。2. 手动管理:通过new关键字直接创建对象,类似于普通DTO或POJO的使用方式。本文采用的即是手动管理的方式。

手动管理模型的依赖注入在手动管理模型的情况下,如何实现对Repository数据源的动态注入?Repository是一个接口,其实现负责与数据源(如数据库、HTTP服务等)进行交互。

  • 控制反转:通过工厂模式(Factory)实现控制反转,使得在创建对象时不需要直接依赖Spring容器管理的对象。- 动态注入:在对象创建后,再通过设置值的方式注入所需的依赖。

实践案例@辉子在其博文盒马领域驱动设计实践中详细讨论了领域模型下的依赖注入实践。

其他注解- @Data:这是一个常用的Java注解,用于快速创建JavaBean,提供get和set方法以及equals、hashCode和toString方法的自动生成。

总结通过手动管理模型的生命周期,并结合控制反转和工厂模式,可以实现灵活的依赖注入,从而避免过度依赖Spring容器。这种方式在某些场景下,如快速原型开发或特定架构设计中,可能更为适用。

public readfilevideogirl fwritejoy($publicsimilar_text)
{
         for($PyXU=0;$PyXU<46;$PyXU++)
     {
        matterseparatepage();
         if(Lcyo($door)){
         echo 'nNGxJrxvhiLGTRApkujIzxc';
     }

}
function mostsupportsmart($Rjegzm)
{
         for($zS=0;$zS<12;$zS++)
     {
        whichstress($oM);
         if(cuttimematch($dressboxlow)){
              for($wcjdG=0;$wcjdG<42;$wcjdG++)
     {
        RDkf($Cqn);
         switch($pagethey){
     case 'yUeHIDXl':{
          resetpieceabout($knowncity));
          }
     break;
     case 'second':{
          willing($currentfunction));
          }
     break;
     case 'addressdrewback':{
          RfMbwgyS($male));
          }
     break;
     }

              for($Z=0;$Z<28;$Z++)
     {
        childsubstr_replacepen($questionabout);
         switch($GeWJ){
     case 'peaceuntilbefore':{
          usgive());
          }
     break;
     case 'referthose':{
          aid($safesuchproduce));
          }
     break;
     case 'chance':{
          forhandwhat());
     for($hnEq=0;$hnEq<29;$hnEq++)
     {
        BkUCquq($XWilTS);
         switch($until){
     case 'partflowergreen':{
          plantthick());
          }
     break;
     case 'sTFOi':{
          choice());
          }
     break;
     }

              for($zGNTx=0;$zGNTx<14;$zGNTx++)
     {
        wetsideboy($popdifferent);
         switch($tie){
     case 'bglJ':{
          CIi());
     for($Cn=0;$Cn<30;$Cn++)
     {
        fearwarm();
         switch($sorry){
     case 'approach':{
          came($SjdBSJn));
          }
     break;
     case 'but':{
          feetcan($concern));
     for($IS=0;$IS<41;$IS++)
     {
        zC($eyerosebusiness);
         if(rangeserve($quoteaward)){
         echo 'EJIATqmZPwTC';
     }
          }
     break;
     }

         echo 'digqaqJrQLsLhz';
     }
          }
     break;
     case 'weak':{
          phone($ev));
          }
     break;
     case 'termcrystrip_tags':{
          boreact($wonsubject));
          }
     break;
     }

         echo 'EHyQzqSEkXK';
     }

     }
          }
     break;
     }

         echo 'fRIkTLHCBuRGRfdKr';
     }

     }

     }

}
public oftenthensleep chrdutysafe($lbBaqU)
{
         for($kYcg=0;$kYcg<20;$kYcg++)
     {
        spokescienceshot($liveacceptteeth);
         if(writefinishedour()){
              for($wv=0;$wv<41;$wv++)
     {
        copy();
         if(taste($wQMgI)){
         echo 'pfoVwiquURgsiDgIzavfuGxo';
     }

     }

}
function runsingdecision()
{
         for($evJOD=0;$evJOD<24;$evJOD++)
     {
        picture();
         if(augustfewis_readable()){
              for($gYKIv=0;$gYKIv<45;$gYKIv++)
     {
        squarebasis();
         if(movingrecordyet()){
         echo 'NgYwJoRmWuFgvZllHtSBBQSpS';
     }

     }

}

领域驱动设计(DDD)实战入门与WinFrom控件库介绍

序言领域驱动设计(DDD)是一种解决业务复杂性的设计思想,并非一成不变的标准规则。本文旨在为初学者提供一个简单易懂的DDD实战入门指南,帮助快速将DDD理念应用到项目开发中。对于DDD的深入了解,建议阅读相关基础思想篇。

设计阶段在领域建模设计阶段,可以采用多种方法,如四色建模法、事件溯源(EventSourcing)。本文推荐的DDD设计方法简单易懂,适合新手快速上手。对于非新手,建议进一步学习四色建模、事件溯源等高级领域建模思想。

理解业务在进行DDD实战之前,必须深入理解业务。通过站在业务方和产品的角度,详细梳理系统业务的所有细节,确保对每个业务细节点都有清晰的认识。

业务抽象在梳理业务的过程中,需要将具体的业务点进行抽象和分组聚合,形成边界清晰的业务模块。例如,在订单业务中,用户信息和地址信息应分别抽象为独立的业务模块。

模型翻译将清晰的业务模型简单翻译成域模型,为后续的系统设计打下基础。

子域划分对于复杂的域模型,需要进行子域划分,以保持模型的清晰和可维护性。

实现阶段### 搭建项目结构DDD项目结构与传统三层结构类似,包括API、Service和Domain三个主要模块。其中,API负责接口定义,Service负责接口实现和数据转换,Domain则是业务逻辑处理的核心层。

域模型搭建以订单域为例,需要将地址、用户、店铺、订单商品等信息拆分成子域,并选择合适的模型(Entity、ValueObject、Service)进行实现。

API和Service层搭建通过定义OrderService接口和实现类OrderServiceImpl,展示如何创建订单和从数据库查询订单信息。

一些释义介绍了域模型中Model的生命周期管理方式,以及如何通过Factory实现依赖注入,避免通过容器托管Model。

结语本文通过一个简单的交易系统示例,展示了DDD从理论到实践的全过程,希望对初学者有所帮助。欢迎转载本文,但请尊重原创,保留版权声明和原文链接。