C#中的领域驱动设计:如何在复杂性中不迷失

面向初学者的在线Python训练器

轻松学习Python,无需理论负担。在浏览器中直接解决带自动检查的实践任务,无需安装任何东西。

开始课程

C#中的领域驱动设计:如何在复杂性中不迷失

\n\n

当您的C#项目超越了“简单的CRUD应用程序”阶段时,关键时刻到来了。业务逻辑开始纠缠不清,服务方法膨胀到数百行,而“重构”这个词会引起神经质的颤抖。熟悉吗?欢迎来到一个没有领域驱动设计(DDD)您就有可能将代码变成泥潭的世界。

\n\n

DDD——不是关于“又一个框架”。它是一种将业务领域置于宇宙中心的哲学。今天,我们将探讨如何在C#项目中应用领域驱动设计,使代码不仅可用,而且清晰灵活。没有废话——只有真实的示例和实用建议。

\n\n

为什么DDD能拯救C#项目?

\n\n

想象一下,您正在为在线商店编写一个订单管理系统。如果您只是将所有逻辑放入ASP.NET控制器中,半年后您将无法解释为什么CreateOrder()方法会调用CalculateShipping(),而后者又会去数据库查询汇率。DDD提供了清晰的分离:有领域层,业务逻辑存在于其中;还有基础设施层,负责处理技术细节。

\n\n

DDD的关键思想是通用语言(Ubiquitous Language)。您和业务专家使用同一种语言交流。在代码中,这体现为使用诸如“Order”、“LineItem”、“DiscountPolicy”这样的术语,而不是“OrderDto”、“OrderService”和“Helper”。代码越接近业务语言,就越容易维护。

\n\n

战略性和战术性设计

\n\n

DDD分为两部分:战略性(如何将系统划分为模块)和战术性(如何在模块内部编写代码)。在C#中,战术性设计为我们提供了四个强大的构建块:

\n\n
    \n
  • Entity — 具有唯一标识符的对象(例如OrderId)。
  • \n
  • Value Object — 由其属性定义的对象(例如Money或Address)。
  • \n
  • Aggregate Root — 管理一组对象的根实体。
  • \n
  • Repository — 数据访问的抽象。
  • \n
\n\n

让我们看看这在代码中是什么样子。

\n\n

示例:设计订单(Order)

\n\n

从一个简单的开始:我们有一个由行项目组成的订单。订单可以处于“新建”、“已支付”或“已取消”状态。

\n\n

1. Value Object: Money

\n\n

金钱是经典的Value Object。两个Money对象如果具有相同的金额和货币,则相等。它们是不可变的。

\n\n
public sealed record Money(decimal Amount, string Currency)\n{\n    public static Money operator +(Money left, Money right)\n    {\n        if (left.Currency != right.Currency)\n            throw new InvalidOperationException("Currency mismatch");\n        return new Money(left.Amount + right.Amount, left.Currency);\n    }\n}\n
\n\n

注意record——在C# 9+中,这是创建Value Object的理想方式。它自动提供Equals、GetHashCode和不可变性。

\n\n

2. Entity: Order和LineItem

\n\n

实体具有标识符。让我们为Entity创建一个基类:

\n\n
public abstract class Entity<TId>\n{\n    public TId Id { get; protected set; }\n\n    protected Entity(TId id) => Id = id;\n\n    public override bool Equals(object? obj) =>\n        obj is Entity<TId> other && Id!.Equals(other.Id);\n\n    public override int GetHashCode() => Id!.GetHashCode();\n}\n
\n\n

现在,Order和LineItem本身:

\n\n
public class Order : Entity<Guid>\n{\n    private readonly List<LineItem> _items = new();\n    public IReadOnlyList<LineItem> Items => _items.AsReadOnly();\n    public OrderStatus Status { get; private set; }\n    public Money Total { get; privat                

博客

书籍推荐