背景
系统开发最难的是职责的合理分配,或者叫:“如何合理的组织代码”,今天说一个关于这方面问题的示例,希望大家多批评。
示例背景
参考数据字典
需求
- OrderCode必须唯一。
- Total = Sum(Subtotal)。
- 订单有三种状态:【未提交】、【待审核】和【已审核】,合理的状态迁移有:【未提交】----》【待审核】和【待审核】----》【已审核】,只有处于【未提交】状态的订单能修改。
- 订单和订单项中的状态必须合法,规则自己定义。
示例实现
项目结构
- 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/下载。