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能夠用來處理跨聚合通訊,也能夠用來處理長事務或離線事務。架構

重點介紹的領域層

採用狀態模式處理狀態遷移。app

 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的同步問題。ide

  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/下載。ui

相關文章
相關標籤/搜索