• DDD:订单管理 之 如何组织代码


    DDD:订单管理 之 如何组织代码

    背景

    系统开发最难的是职责的合理分配,或者叫:“如何合理的组织代码”,今天说一个关于这方面问题的示例,希望大家多批评。

    示例背景

    参考数据字典

    需求

    1. OrderCode必须唯一。
    2. Total = Sum(Subtotal)。
    3. 订单有三种状态:【未提交】、【待审核】和【已审核】,合理的状态迁移有:【未提交】----》【待审核】和【待审核】----》【已审核】,只有处于【未提交】状态的订单能修改。
    4. 订单和订单项中的状态必须合法,规则自己定义。

    示例实现

    项目结构

    • Application:应用层,负责领域逻辑的封装。主要角色:ApplicationService、CommandHandler。
    • Boostrap:启动管理层,负责启动过程管理,如:注册Ioc、初始化配置。主要角色:BootstrapListener。
    • Commands:命令层,是一个契约层。主要角色:Comamnd、DTO。
    • Controllers:控制器层,边界层。主要角色:Controller。
    • Domain:领域层,负责领域逻辑的组织。主要角色:Aggregate、Entity、ValueObject、Factory、DomainService、IRepository、IUnitOfWork。
    • Events:事件层,是一个契约层,跨聚合流程可以采用。主要角色:Event。
    • EventSubscribers:事件监听层。主要角色:EventSubscriber。
    • Infrastructure:基础设施层。主要角色:Repository、QueryService、UnitOfWork。
    • Query:查询层,为UI的查询提供服务,主要角色:QueryService。

    项目整体采用简单的CQRS架构,Command端采用DDD组织,Query直接从数据库返回dynamic类型。Event可以用来处理跨聚合通信,也可以用来处理长事务或离线事务。

    重点介绍的领域层

    采用状态模式处理状态迁移。

    复制代码
     1 using System;
     2 using System.Collections.Generic;
     3 using System.Linq;
     4 using System.Text;
     5 using System.Threading.Tasks;
     6 
     7 namespace Happy.Example.Domain.TestOrders
     8 {
     9     public interface ITestOrderState
    10     {
    11         void AddTestOrderDetail(TestOrderDetail testOrderDetail);
    12 
    13         void UpdateTestOrderDetail(Guid testOrderDetailId, decimal subtotal);
    14 
    15         void RemoveTestOrderDetail(Guid testOrderDetailId);
    16 
    17         void Commit();
    18 
    19         void Verify();
    20 
    21         string Status { get; }
    22 
    23         TestOrder TestOrder { set; }
    24     }
    25 }
    复制代码
    复制代码
     1 using System;
     2 using System.Collections.Generic;
     3 using System.Linq;
     4 using System.Text;
     5 using System.Threading.Tasks;
     6 
     7 namespace Happy.Example.Domain.TestOrders
     8 {
     9     public partial class TestOrder
    10     {
    11         private abstract class TestOrderState : ITestOrderState
    12         {
    13             public virtual void AddTestOrderDetail(TestOrderDetail testOrderDetail)
    14             {
    15                 this.ThrowInvalidOperationException();
    16             }
    17 
    18             public virtual void UpdateTestOrderDetail(Guid testOrderDetailId, decimal subtotal)
    19             {
    20                 this.ThrowInvalidOperationException();
    21             }
    22 
    23             public virtual void RemoveTestOrderDetail(Guid testOrderDetailId)
    24             {
    25                 this.ThrowInvalidOperationException();
    26             }
    27 
    28             public virtual void Commit()
    29             {
    30                 this.ThrowInvalidOperationException();
    31             }
    32 
    33             public virtual void Verify()
    34             {
    35                 this.ThrowInvalidOperationException();
    36             }
    37 
    38             public abstract string Status { get; }
    39 
    40             public TestOrder TestOrder { protected get; set; }
    41 
    42             private void ThrowInvalidOperationException()
    43             {
    44                 throw new InvalidOperationException(string.Format("处于【{0}】的订单不能执行此操作!", this.Status));
    45             }
    46         }
    47     }
    48 }
    复制代码
    复制代码
     1 using System;
     2 using System.Collections.Generic;
     3 using System.Linq;
     4 using System.Text;
     5 using System.Threading.Tasks;
     6 
     7 namespace Happy.Example.Domain.TestOrders
     8 {
     9     public partial class TestOrder
    10     {
    11         private sealed class UnCommitted : TestOrderState
    12         {
    13             internal static readonly string UnCommittedStatus = "未提交";
    14 
    15             public override void AddTestOrderDetail(TestOrderDetail testOrderDetail)
    16             {
    17                 this.TestOrder.DoAddTestOrderDetail(testOrderDetail);
    18             }
    19 
    20             public override void UpdateTestOrderDetail(Guid testOrderDetailId, decimal subtotal)
    21             {
    22                 this.TestOrder.DoUpdateTestOrderDetail(testOrderDetailId, subtotal);
    23             }
    24 
    25             public override void RemoveTestOrderDetail(Guid testOrderDetailId)
    26             {
    27                 this.TestOrder.DoRemoveTestOrderDetail(testOrderDetailId);
    28             }
    29 
    30             public override void Commit()
    31             {
    32                 this.TestOrder.DoCommit();
    33             }
    34 
    35             public override string Status
    36             {
    37                 get { return UnCommittedStatus; }
    38             }
    39         }
    40     }
    41 }
    复制代码

    采用封装集合手法处理Total的同步问题。

    复制代码
      1 using System;
      2 using System.Collections.Generic;
      3 using System.Linq;
      4 using System.Text;
      5 using System.Threading.Tasks;
      6 
      7 using Happy.Domain;
      8 using Happy.Domain.Tree;
      9 using Happy.Infrastructure.ExtentionMethods;
     10 using Happy.Example.Events.TestOrders;
     11 
     12 namespace Happy.Example.Domain.TestOrders
     13 {
     14     public partial class TestOrder : AggregateRoot<Guid>
     15     {
     16         private ITestOrderState _orderState;
     17 
     18         protected TestOrder() { }
     19 
     20         public TestOrder(string orderCode, string customer)
     21         {
     22             orderCode.MustNotNullAndNotWhiteSpace("orderCode");
     23             customer.MustNotNullAndNotWhiteSpace("customer");
     24 
     25             this.Id = Guid.NewGuid();
     26             this.OrderCode = orderCode;
     27             this.Customer = customer;
     28             this.OrderState = TestOrderStateFactory.CreateUnCommittedTestOrderState(this);
     29             this.TestOrderDetails = new List<TestOrderDetail>();
     30         }
     31 
     32         public virtual System.String OrderCode { get; protected set; }
     33         public virtual System.String Customer { get; protected set; }
     34         public virtual System.Decimal Total { get; protected set; }
     35         public virtual System.String Status { get; protected set; }
     36         public virtual ICollection<TestOrderDetail> TestOrderDetails { get; protected set; }
     37 
     38         private ITestOrderState OrderState
     39         {
     40             get
     41             {
     42                 if (_orderState == null)
     43                 {
     44                     _orderState = TestOrderStateFactory.Create(this, this.Status);
     45                 }
     46 
     47                 return _orderState;
     48             }
     49             set
     50             {
     51                 _orderState = value;
     52                 this.Status = value.Status;
     53             }
     54         }
     55 
     56         public void AddTestOrderDetail(TestOrderDetail testOrderDetail)
     57         {
     58             this.OrderState.AddTestOrderDetail(testOrderDetail);
     59         }
     60 
     61         public void UpdateTestOrderDetail(Guid testOrderDetailId, decimal subtotal)
     62         {
     63             this.OrderState.UpdateTestOrderDetail(testOrderDetailId, subtotal);
     64         }
     65 
     66         public void RemoveTestOrderDetail(Guid testOrderDetailId)
     67         {
     68             this.OrderState.RemoveTestOrderDetail(testOrderDetailId);
     69         }
     70 
     71         public void Commit()
     72         {
     73             this.OrderState.Commit();
     74         }
     75 
     76         public void Verify()
     77         {
     78             this.OrderState.Verify();
     79         }
     80 
     81         private void DoAddTestOrderDetail(TestOrderDetail testOrderDetail)
     82         {
     83             this.TestOrderDetails.Add(testOrderDetail);
     84 
     85             this.Total += testOrderDetail.Subtotal;
     86         }
     87 
     88         private void DoUpdateTestOrderDetail(Guid testOrderDetailId, decimal subtotal)
     89         {
     90             var testOrderDetail = this.TestOrderDetails.First(x => x.Id == testOrderDetailId);
     91 
     92             this.Total -= testOrderDetail.Subtotal;
     93             testOrderDetail.Subtotal = subtotal;
     94             this.Total += testOrderDetail.Subtotal;
     95         }
     96 
     97         private void DoRemoveTestOrderDetail(Guid testOrderDetailId)
     98         {
     99             var testOrderDetail = this.TestOrderDetails.First(x => x.Id == testOrderDetailId);
    100 
    101             this.TestOrderDetails.Remove(testOrderDetail);
    102             this.Total -= testOrderDetail.Subtotal;
    103         }
    104 
    105         private void DoCommit()
    106         {
    107             this.OrderState = TestOrderStateFactory.CreatePendingVerificationTestOrderState(this);
    108         }
    109 
    110         private void DoVerify()
    111         {
    112             this.OrderState = TestOrderStateFactory.CreateVerifiedTestOrderState(this);
    113 
    114             this.PublishEvent(new TestOrderVerified());
    115         }
    116     }
    117 }
    复制代码

    效果图

     

    背景

    写这个简单的Demo过程,遇到了很多小的决策,这篇文章就看做一个开头吧,后边重点介绍每个决策点,在一篇文章中真的很难说完,喜欢看代码的朋友,先去https://happy.codeplex.com/下载。

     
    分类: DDD
  • 相关阅读:
    JAVA日期与时间
    CSS的重点知识
    java使用深度优先遍历算法的算法题
    使用python命令行参数的例子
    JAVA中的BigInteger与BigDecimal类功能强大
    软件工程第一次作业
    ORACLE数据库知识点整理
    看懂PL/SQL执行计划
    Oracle Hints详解
    oracle创建用户、授权及角色管理
  • 原文地址:https://www.cnblogs.com/Leo_wl/p/3169111.html
Copyright © 2020-2023  润新知