背景

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

示例背景

参考数据字典

需求

  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可以用来处理跨聚合通信,也可以用来处理长事务或离线事务。

重点介绍的领域层

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

 using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks; namespace Happy.Example.Domain.TestOrders
{
public interface ITestOrderState
{
void AddTestOrderDetail(TestOrderDetail testOrderDetail); void UpdateTestOrderDetail(Guid testOrderDetailId, decimal subtotal); void RemoveTestOrderDetail(Guid testOrderDetailId); void Commit(); void Verify(); string Status { get; } TestOrder TestOrder { set; }
}
}
 using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks; namespace Happy.Example.Domain.TestOrders
{
public partial class TestOrder
{
private abstract class TestOrderState : ITestOrderState
{
public virtual void AddTestOrderDetail(TestOrderDetail testOrderDetail)
{
this.ThrowInvalidOperationException();
} public virtual void UpdateTestOrderDetail(Guid testOrderDetailId, decimal subtotal)
{
this.ThrowInvalidOperationException();
} public virtual void RemoveTestOrderDetail(Guid testOrderDetailId)
{
this.ThrowInvalidOperationException();
} public virtual void Commit()
{
this.ThrowInvalidOperationException();
} public virtual void Verify()
{
this.ThrowInvalidOperationException();
} public abstract string Status { get; } public TestOrder TestOrder { protected get; set; } private void ThrowInvalidOperationException()
{
throw new InvalidOperationException(string.Format("处于【{0}】的订单不能执行此操作!", this.Status));
}
}
}
}
 using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks; namespace Happy.Example.Domain.TestOrders
{
public partial class TestOrder
{
private sealed class UnCommitted : TestOrderState
{
internal static readonly string UnCommittedStatus = "未提交"; public override void AddTestOrderDetail(TestOrderDetail testOrderDetail)
{
this.TestOrder.DoAddTestOrderDetail(testOrderDetail);
} public override void UpdateTestOrderDetail(Guid testOrderDetailId, decimal subtotal)
{
this.TestOrder.DoUpdateTestOrderDetail(testOrderDetailId, subtotal);
} public override void RemoveTestOrderDetail(Guid testOrderDetailId)
{
this.TestOrder.DoRemoveTestOrderDetail(testOrderDetailId);
} public override void Commit()
{
this.TestOrder.DoCommit();
} public override string Status
{
get { return UnCommittedStatus; }
}
}
}
}

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

 using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks; using Happy.Domain;
using Happy.Domain.Tree;
using Happy.Infrastructure.ExtentionMethods;
using Happy.Example.Events.TestOrders; namespace Happy.Example.Domain.TestOrders
{
public partial class TestOrder : AggregateRoot<Guid>
{
private ITestOrderState _orderState; protected TestOrder() { } public TestOrder(string orderCode, string customer)
{
orderCode.MustNotNullAndNotWhiteSpace("orderCode");
customer.MustNotNullAndNotWhiteSpace("customer"); this.Id = Guid.NewGuid();
this.OrderCode = orderCode;
this.Customer = customer;
this.OrderState = TestOrderStateFactory.CreateUnCommittedTestOrderState(this);
this.TestOrderDetails = new List<TestOrderDetail>();
} public virtual System.String OrderCode { get; protected set; }
public virtual System.String Customer { get; protected set; }
public virtual System.Decimal Total { get; protected set; }
public virtual System.String Status { get; protected set; }
public virtual ICollection<TestOrderDetail> TestOrderDetails { get; protected set; } private ITestOrderState OrderState
{
get
{
if (_orderState == null)
{
_orderState = TestOrderStateFactory.Create(this, this.Status);
} return _orderState;
}
set
{
_orderState = value;
this.Status = value.Status;
}
} public void AddTestOrderDetail(TestOrderDetail testOrderDetail)
{
this.OrderState.AddTestOrderDetail(testOrderDetail);
} public void UpdateTestOrderDetail(Guid testOrderDetailId, decimal subtotal)
{
this.OrderState.UpdateTestOrderDetail(testOrderDetailId, subtotal);
} public void RemoveTestOrderDetail(Guid testOrderDetailId)
{
this.OrderState.RemoveTestOrderDetail(testOrderDetailId);
} public void Commit()
{
this.OrderState.Commit();
} public void Verify()
{
this.OrderState.Verify();
} private void DoAddTestOrderDetail(TestOrderDetail testOrderDetail)
{
this.TestOrderDetails.Add(testOrderDetail); this.Total += testOrderDetail.Subtotal;
} private void DoUpdateTestOrderDetail(Guid testOrderDetailId, decimal subtotal)
{
var testOrderDetail = this.TestOrderDetails.First(x => x.Id == testOrderDetailId); this.Total -= testOrderDetail.Subtotal;
testOrderDetail.Subtotal = subtotal;
this.Total += testOrderDetail.Subtotal;
} private void DoRemoveTestOrderDetail(Guid testOrderDetailId)
{
var testOrderDetail = this.TestOrderDetails.First(x => x.Id == testOrderDetailId); this.TestOrderDetails.Remove(testOrderDetail);
this.Total -= testOrderDetail.Subtotal;
} private void DoCommit()
{
this.OrderState = TestOrderStateFactory.CreatePendingVerificationTestOrderState(this);
} private void DoVerify()
{
this.OrderState = TestOrderStateFactory.CreateVerifiedTestOrderState(this); this.PublishEvent(new TestOrderVerified());
}
}
}

效果图

背景

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

DDD:订单管理 之 如何组织代码的更多相关文章

  1. RDIFramework.NET V3.3 WinForm版新增订单管理主从表事例

    功能描述 无论什么系统,除了常规的单表处理外,主从表的应用都是非常普遍的,RDIFramework.NET V3.3 WinForm版本中新增了一个主从表的事例供大家参考.主从表的界面设计大同小异,主 ...

  2. Flask实战-留言板-安装虚拟环境、使用包组织代码

    Flask实战 留言板 创建项目目录messageboard,从GreyLi的代码中把Pipfile和Pipfile.lock文件拷贝过来,这两个文件中定义了虚拟环境中需要安装的包的信息和位置,进入m ...

  3. 一个ssm综合小案例-商品订单管理----写在前面

    学习了这么久,一直都是零零散散的,没有把知识串联起来综合运用一番 比如拦截器,全局异常处理,json 交互,RESTful 等,这些常见技术必须要掌握 接下来呢,我就打算通过这么一个综合案例把这段时间 ...

  4. Unity3D如何有效地组织代码?(转)

    问题: Unity3D可以说是高度的Component-Based Architecture,同时它的库提供了大量的全局变量.如何来组织代码呢? 答: - Unity有一些自身的约定,譬如项目里的Ed ...

  5. 【共享单车】—— React后台管理系统开发手记:城市管理和订单管理

    前言:以下内容基于React全家桶+AntD实战课程的学习实践过程记录.最终成果github地址:https://github.com/66Web/react-antd-manager,欢迎star. ...

  6. Unity3D如何有效地组织代码?

    本文整理自知乎,原文链接:http://www.zhihu.com/question/21070379 问题: Unity3D可以说是高度的Component-Based Architecture,同 ...

  7. 使用 Git 来管理 Xcode 中的代码片段

    使用 Git 来管理 Xcode 中的代码片段 代码片段介绍 xcode4 引入了一个新 feature: code snippets,在整个界面的右下角,可以通过快捷键:cmd + ctrl + o ...

  8. 如何管理你的 Javascript 代码

    今天不聊技术的问题,咱们来聊聊在前端开发中如何管理好自己的 Javascript 代码.首先,咱们先来说说一般都有哪些管理方式?我相信  seajs . requirejs  对于前端开发者而言都不陌 ...

  9. Python基础-修改excel、redis、接口开发、组织代码

    pymysql模块补充内容 1. 游标.description():显示表的字段属性 (什么是游标:游标用于交互式应用,就好比word里的光标一样,要修改某个地方,要先把光标移动到这里) 用好这个方法 ...

随机推荐

  1. Android之把手机的3g流量共享出来让其他人连接这个wifi

    转自:http://blog.csdn.net/luoboo525/article/details/7883998   亲测可用 用过快牙的朋友应该知道它们在两天设备之间传输文件的时候使用的是wifi ...

  2. 从《BLAME!》说开去——新一代生产级卡通真实感混合的渲染方案

    <BLAME!>是Polygon Pictures Inc.(以下简称PPI)创业33周年以来制作的第一部CG剧场电影,故事来自于贰瓶勉的同名漫画作品(中文译名为<探索者>或者 ...

  3. 解决Electron加载带jquery的项目报错问题

    <!-- Insert this line above script imports --> <script>if (typeof module === 'object') { ...

  4. nginx-upstream-keepalive;accept_mutex-proxy_http_version-1.1-proxy_set_header-connection

    nginx+resin+redis+mysql的架构: 有外部模拟用户请求的大量攻击:它尝试请求了80就断开:看到的现象是在跑有nginx80的主机上TCP连接数少 : ESTABLISHED少: 在 ...

  5. ASP.NET MVC 分部视图

    @model PartViewDemo.Models.HomeInfo@using PartViewDemo.Models;@{ ViewBag.Title = "Index";} ...

  6. EF OnModelCreating

      http://www.cnblogs.com/libingql/p/3353112.html     protected override void OnModelCreating(DbModel ...

  7. Java Inner Classes

    When thinking about inner classes in java, the first thing that comes to my mind is that, WHY do we ...

  8. navicat 链接linux 服务器上的数据库

  9. Hadoop - 任务调度系统比较

    1.概述 在Hadoop应用,随着业务指标的迭代,而使其日趋复杂化的时候,管理Hadoop的相关应用会变成一件头疼的事情,如:作业的依赖调度,任务的运行情况的监控,异常问题的排查等,这些问题会是的我们 ...

  10. Unity数据存储路径总结

    一.在项目根目录中创建Resources文件夹来保存文件.可以使用Resources.Load("文件名字,注:不包括文件后缀名");把文件夹中的对象加载出来.注:此方可实现对文件 ...