Fork me on GitHub
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
posted on 2013-07-03 11:25  HackerVirus  阅读(300)  评论(0编辑  收藏  举报