news 2026/9/14 10:18:16

领域驱动设计与模块化单体实战术语全解:MyMeetings 仓库架构词汇表深度指南

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
领域驱动设计与模块化单体实战术语全解:MyMeetings 仓库架构词汇表深度指南

领域驱动设计与模块化单体实战术语全解:MyMeetings 仓库架构词汇表深度指南

【免费下载链接】modular-monolith-with-dddFull Modular Monolith application with Domain-Driven Design approach.项目地址: https://gitcode.com/GitHub_Trending/mo/modular-monolith-with-ddd

本文是一份面向 .NET 开发者的领域驱动设计(DDD)术语实战指南。它以 modular-monolith-with-ddd 仓库docs/catalog-of-terms/目录下的完整术语表为主线,逐一讲解 Aggregate、Entity、Value Object、Domain Event、Command、Decorator、Strategy、Dependency Injection 等核心概念的定义、源码实现与业务场景,并对照真实代码给出可验证的落地方案。读完本文,你将能把这些模式直接映射到自己的模块化单体项目中,理解 MyMeetings 中"模块内部富领域模型 + 模块间事件驱动"的整体设计思路。

MyMeetings 模块化单体架构中模块级别结构示意

一、术语表的定位:一份可点击学习的 DDD 词汇索引

在深入具体模式之前,先了解这份术语表本身的结构。docs/catalog-of-terms/README.md是一份带超链接的索引,将 DDD、CQRS、事件驱动与软件工程中约 80 个高频术语集中列出。其中有相当一部分术语配有独立子目录,内含"定义(Definition)— 模型图(Model)— 代码示例(Code)— 解读(Description)"四段式卡片,例如:

  • docs/catalog-of-terms/Aggregate-DDD/README.md
  • docs/catalog-of-terms/Command/README.md
  • docs/catalog-of-terms/Decorator-Pattern/README.md
  • docs/catalog-of-terms/Dependency-Injection/README.md
  • docs/catalog-of-terms/Domain-Event/README.md
  • docs/catalog-of-terms/Entity-DDD/README.md
  • docs/catalog-of-terms/Event/README.md
  • docs/catalog-of-terms/Event-Sourcing/README.md
  • docs/catalog-of-terms/Event-Storming/README.md
  • docs/catalog-of-terms/Integration-Event/README.md
  • docs/catalog-of-terms/Strategy-Pattern/README.md
  • docs/catalog-of-terms/ValueObject-DDD/README.md

每张卡片还配有对应的 PlantUML 源文件(如docs/catalog-of-terms/Aggregate-DDD/aggregate-ddd.puml),体现"Diagram as text"——模型图本身也是可版本化、可评审的文本资产。

docs/architecture-decision-log/下的 ADR 文件则记录了这些术语对应的架构决策,例如0007-use-cqrs-architectural-style.md0011-create-rich-domain-models.md0012-use-domain-driven-design-tactical-patterns.md。阅读术语表时配合 ADR,可以看到"模式名词"与"项目决策"之间的一一对应关系。

下面按主题分组,深入展开这些术语在 MyMeetings 中的真实实现。

二、DDD 战术模式三件套:Entity、Value Object 与 Aggregate

2.1 Entity(DDD):靠身份而非属性区分对象

当一个对象以身份(identity)而非属性被区分时,应把身份作为其模型定义的核心。保持类定义简洁,聚焦生命周期连续性与身份。

这是 Evans《领域驱动设计》对 Entity 的经典定义。判断依据很朴素:是否需要在时间上持续追踪它

MyMeetings 中MeetingGroup(会议小组)就是典型 Entity——它从提案被接受、创建、成员加入/退出、到设置到期时间,拥有完整生命周期,因此必须拥有全局唯一标识Id。源码位于 src/Modules/Meetings/Domain/MeetingGroups/MeetingGroup.cs:

public class MeetingGroup : Entity, IAggregateRoot { public MeetingGroupId Id { get; private set; } private string _name; private string _description; private MeetingGroupLocation _location; private MemberId _creatorId; private List<MeetingGroupMember> _members; private DateTime _createDate; private DateTime? _paymentDateTo; ... }

注意几个实现细节:

  • 无公开 setter,完全封装。所有属性都是private字段,外部只能通过暴露的"行为方法"改变状态。Id的 setter 也是private,只在构造时赋值。
  • private无参构造函数仅用于 EF("Only for EF"),防止外部直接new出无效实体。
  • internal static工厂方法(如CreateBasedOnProposal)作为唯一创建入口,配合私有构造函数保证不变量。

这正是术语卡所强调的:Entity 应当"fully encapsulated - you can only mutate its state via exposed behavior (no setters)"。

实体基类位于 src/BuildingBlocks/Domain/Entity.cs,提供了AddDomainEventCheckRule两个受保护方法,是所有实体共享的"基建":

public abstract class Entity { private List<IDomainEvent> _domainEvents; public IReadOnlyCollection<IDomainEvent> DomainEvents => _domainEvents?.AsReadOnly(); public void ClearDomainEvents() { _domainEvents?.Clear(); } protected void AddDomainEvent(IDomainEvent domainEvent) { _domainEvents ??= []; this._domainEvents.Add(domainEvent); } protected void CheckRule(IBusinessRule rule) { if (rule.IsBroken()) { throw new BusinessRuleValidationException(rule); } } }

可以看到,实体层已经内建了"记录领域事件 + 校验业务规则"的能力,这与 src/BuildingBlocks/Domain/IBusinessRule.cs、src/BuildingBlocks/Domain/BusinessRuleValidationException.cs 共同构成规则校验的骨架。

2.2 Value Object(DDD):不可变、无身份、按属性比较

当只关心模型中某个元素的属性时,把它归类为 VALUE OBJECT。使其表达属性所承载的含义并提供相关功能。将 VALUE OBJECT 视为不可变对象,不赋予它身份。

MyMeetings 中最典型的 Value Object 是MoneyValue(金额),源码位于 src/Modules/Meetings/Domain/Meetings/MoneyValue.cs:

public class MoneyValue : ValueObject { public decimal Value { get; } public string Currency { get; } private MoneyValue(decimal value, string currency) { this.Value = value; this.Currency = currency; } public static MoneyValue Of(decimal value, string currency) { CheckRule(new ValueOfMoneyMustNotBeNegativeRule(value)); return new MoneyValue(value, currency); } // 运算符重载,支持 decimal 与 MoneyValue 的比较 public static bool operator >(decimal left, MoneyValue right) => left > right.Value; public static bool operator <(decimal left, MoneyValue right) => left < right.Value; ... }

为什么MoneyValue是 Value Object 而非 Entity?

  1. 不需要在时间上追踪——金额没有生命周期;
  2. 没有身份——我们从不问"这个 100 元是谁";
  3. 不可变——ValueCurrency只有get,唯一构造入口是带规则校验的静态工厂Of
  4. 按属性比较——两个MoneyValue只要ValueCurrency相等就视为相等。

Value Object 的相等性比较由基类 src/BuildingBlocks/Domain/ValueObject.cs 通过反射统一实现:遍历类型的所有公开属性与非公开字段,逐一比较值并生成哈希码,同时支持==/!=运算符重载,还可用IgnoreMemberAttribute忽略不应参与比较的成员:

public override bool Equals(object obj) { if (obj == null || GetType() != obj.GetType()) return false; return GetProperties().All(p => PropertiesAreEqual(obj, p)) && GetFields().All(f => FieldsAreEqual(obj, f)); }

2.3 Aggregate(DDD):以聚合根为边界的封装与不变量保障

将 ENTITY 和 VALUE OBJECT 聚类为 AGGREGATE 并为其定义边界。为每个 AGGREGATE 选择一个 ENTITY 作为根,并通过根控制对边界内所有对象的访问。

聚合是 DDD 战术模式中最重要也最难落地的一个。在 MyMeetings 中,MeetingGroup不仅是一个 Entity,更是整个聚合的聚合根(Aggregate Root)

  • 聚合成员MeetingGroup(根)、MeetingGroupLocation(值对象)、MeetingGroupIdMeetingGroupMemberMeetingGroupMemberRole
  • 边界控制:外部只能访问根对象MeetingGroup,其余成员全部私有封装;
  • 不变量优先:每个公开方法第一件事就是CheckRule校验业务规则。

以成员加入为例(MeetingGroup.cs):

public void JoinToGroupMember(MemberId memberId) { this.CheckRule(new MeetingGroupMemberCannotBeAddedTwiceRule(_members, memberId)); this._members.Add(MeetingGroupMember.CreateNew(this.Id, memberId, MeetingGroupMemberRole.Member)); }

MeetingGroupMemberCannotBeAddedTwiceRule先检查"同一成员不能重复加入",规则通过后才修改状态。这正对应 Evans 所说的 "the root controls access, it cannot be blindsided by changes to the internals"——聚合根控制所有访问,因此不会被内部成员的意外变更所"蒙蔽"。

再以创建会议为例,CreateMeeting同时校验两条业务规则:

public Meeting CreateMeeting(string title, MeetingTerm term, string description, MeetingLocation location, int? attendeesLimit, int guestsLimit, Term rsvpTerm, MoneyValue eventFee, List<MemberId> hostsMembersIds, MemberId creatorId) { this.CheckRule(new MeetingCanBeOrganizedOnlyByPayedGroupRule(_paymentDateTo)); this.CheckRule(new MeetingHostMustBeAMeetingGroupMemberRule(creatorId, hostsMembersIds, _members)); return Meeting.CreateNew(this.Id, title, term, description, location, MeetingLimits.Create(attendeesLimit, guestsLimit), rsvpTerm, eventFee, hostsMembersIds, creatorId); }

"只有已付费的小组才能组织会议"(MeetingCanBeOrganizedOnlyByPayedGroupRule)与"会议主办者必须是小组成员"(MeetingHostMustBeAMeetingGroupMemberRule)这两条不变量,在聚合根层面一次性保障。对应规则实现位于 src/Modules/Meetings/Domain/MeetingGroups/Rules/。

聚合根还负责在状态变更时发出领域事件,例如构造函数中AddDomainEvent(new MeetingGroupCreatedDomainEvent(this.Id, creatorId)),把"小组已创建"这一事实记录下来。

小结:Entity 回答"我是什么身份",Value Object 回答"我的属性是什么",Aggregate 则回答"谁能改我、改之前必须满足什么"。三者共同构成了 src/BuildingBlocks/Domain/IAggregateRoot.cs 标记接口所表达的"富领域模型"。

三、Command:把"意图"显式化

Command 是"请求做某事"的表达,它代表系统用户关于系统将如何改变其状态的意图。

Command 有三个重要特征(来自 Open Agile Architecture):

  • 结果只有成功或失败,成功的结果是事件(Event(s));
  • 成功时必然发生状态变更(否则等于什么都没发生);
  • 命名规范:用动词(现在时或不定式)+ 来自领域的名词词组,例如CancelMeetingBuySubscription

MyMeetings 中 Command 以两种形态出现:

3.1 应用层 Command 对象 + Handler(参数对象模式)

以"取消会议"为例,命令对象位于 src/Modules/Meetings/Application/Meetings/CancelMeeting/CancelMeetingCommand.cs:

public class CancelMeetingCommand : CommandBase { public CancelMeetingCommand(Guid meetingId) { MeetingId = meetingId; } public Guid MeetingId { get; } }

命令处理器位于 src/Modules/Meetings/Application/Meetings/CancelMeeting/CancelMeetingCommandHandler.cs:

internal class CancelMeetingCommandHandler : ICommandHandler<CancelMeetingCommand> { private readonly IMeetingRepository _meetingRepository; private readonly IMemberContext _memberContext; internal CancelMeetingCommandHandler(IMeetingRepository meetingRepository, IMemberContext memberContext) { _meetingRepository = meetingRepository; _memberContext = memberContext; } public async Task Handle(CancelMeetingCommand request, CancellationToken cancellationToken) { var meeting = await _meetingRepository.GetByIdAsync(new MeetingId(request.MeetingId)); meeting.Cancel(_memberContext.MemberId); } }

Handler 的三步曲非常清晰:加载聚合 → 调用聚合行为方法 → 由 UnitOfWork 统一提交。它自身不实现任何业务逻辑,业务逻辑全部封装在聚合内部。

3.2 聚合上的命令方法(DDD 形态)

同一业务在聚合上体现为行为方法(src/Modules/Meetings/Domain/Meetings/Meeting.cs#L278-L290):

public void Cancel(MemberId cancelMemberId) { this.CheckRule(new MeetingCannotBeChangedAfterStartRule(_term)); if (!_isCanceled) { _isCanceled = true; _cancelDate = SystemClock.Now; _cancelMemberId = cancelMemberId; this.AddDomainEvent(new MeetingCanceledDomainEvent(this.Id, _cancelMemberId, _cancelDate.Value)); } }

3.3 命令可以"被拒绝":失败即回滚

术语卡强调了一个关键点:Command 在状态改变之前是可以被拒绝的。两条拒绝路径:

  • Handler 层:如MeetingId无效,仓储加载不到聚合,直接抛异常;
  • 领域层:业务规则被破坏,CheckRule抛出BusinessRuleValidationException

无论哪条路径,命令都被拒绝,所有未提交的变更随之回滚(状态不变)。这个"全有或全无"语义由基础设施层的事务装饰器保证,见 src/BuildingBlocks/Infrastructure/DomainEventsDispatching/UnitOfWorkCommandHandlerDecorator.cs:

public async Task Handle(T command, CancellationToken cancellationToken) { await this._decorated.Handle(command, cancellationToken); await this._unitOfWork.CommitAsync(cancellationToken); }

Handler 执行成功后统一CommitAsync,任何异常都不会进入提交阶段。

3.4 Command 的两种边界:用户命令与内部命令

从源码结构看,Meetings 模块还区分了"外部命令"与"内部命令"两种载体。内部命令(InternalCommandBase,位于 src/Modules/Meetings/Application/Configuration/Commands/InternalCommandBase.cs)用于异步处理流程(如发邮件、订阅到期检查等),配合 Outbox / 定时调度机制消费,是模块内部实现"命令队列"的基建。

四、Domain Event 与 Integration Event:两种事件的分工

4.1 Event 的定义与分类

事件是发生在过去的事情。

这是最简洁也最根本的定义。因为发生在过去,事件天然不可变只能追加。在 MyMeetings 中事件分为两类:

  • Domain Event(领域事件):发生在领域内、需要同一进程内其他部分感知的事件;
  • Integration Event(集成事件):用于模块之间(跨进程/跨边界)异步通信的事件。

4.2 Domain Event:进程内的"广播"

以购买订阅为例,SubscriptionPayment聚合根在创建支付记录时发出SubscriptionPaymentCreatedDomainEvent(位于 src/Modules/Payments/Domain/SubscriptionPayments/Events/ 对应文件):

public class SubscriptionPaymentCreatedDomainEvent : DomainEventBase { public Guid SubscriptionPaymentId { get; } public Guid PayerId { get; } public string SubscriptionPeriodCode { get; } public string CountryCode { get; } public string Status { get; } public decimal Value { get; } public string Currency { get; } ... }

事件基类 src/BuildingBlocks/Domain/DomainEventBase.cs 提供通用字段:

public class DomainEventBase : IDomainEvent { public Guid Id { get; } public DateTime OccurredOn { get; } public DomainEventBase() { this.Id = Guid.NewGuid(); this.OccurredOn = DateTime.UtcNow; } }
  • Id:事件自身的自动生成唯一标识;
  • OccurredOn:事件发生的时刻(UTC)。

领域事件的所有属性都是getonly——事件是过去的事实,你无法改变过去

领域事件的分发由 src/BuildingBlocks/Infrastructure/DomainEventsDispatching/DomainEventsDispatcher.cs 统一负责:在每次命令提交前,从聚合根收集DomainEvents,再通过内存事件总线分发给进程内的处理器(notification handler)。这正是 ADR0014-event-driven-communication-between-modules.md0015-use-in-memory-events-bus.md的落地。

4.3 Integration Event:模块间通信的"契约"

领域事件不出模块边界;跨模块必须走集成事件。例如 src/Modules/Meetings/IntegrationEvents/MeetingGroupProposedIntegrationEvent.cs、src/Modules/Meetings/IntegrationEvents/MemberCreatedIntegrationEvent.cs 等,均定义在独立的IntegrationEvents程序集中,供其他模块引用。

典型的跨模块流程:

  1. Meetings 模块领域层发出MeetingGroupProposedDomainEvent
  2. 基础设施层将其转换为MeetingGroupProposedIntegrationEvent(映射逻辑见 src/BuildingBlocks/Infrastructure/DomainEventsDispatching/DomainNotificationsMapper.cs);
  3. 通过 Outbox(发件箱)持久化并异步投递;
  4. Administration 模块的MeetingGroupProposedIntegrationEventHandler(src/Modules/Administration/Application/MeetingGroupProposals/MeetingGroupProposedIntegrationEventHandler.cs)消费该事件,创建对应的待审核提案。

这套机制保证了模块间的最终一致性:发送方与接收方各自在本地事务中完成写入,事件通过 src/BuildingBlocks/Infrastructure/EventBus/InMemoryEventBus.cs 与 Outbox 异步传递。

五、行为型与结构型模式:Decorator、Strategy、Dependency Injection

5.1 Dependency Injection:依赖由外部注入而非自行创建

依赖注入是一种技术:对象接收它所依赖的其他对象,这些对象被称为依赖。

CancelMeetingCommandHandler需要两个协作者:会议仓储IMeetingRepository和成员上下文IMemberContext。它不自己new实现,而是通过构造函数注入接收接口:

internal CancelMeetingCommandHandler(IMeetingRepository meetingRepository, IMemberContext memberContext) { _meetingRepository = meetingRepository; _memberContext = memberContext; }

这样带来的好处是:Handler 依赖的是抽象接口而非具体实现,测试时可以轻松替换为 Mock/Stub。这正是 src/Modules/Meetings/Application/Meetings/CancelMeeting/CancelMeetingCommandHandler.cs 的实际写法。同时,仓库为每个模块建立独立 IoC 容器(ADR0016-create-ioc-container-per-module.md),模块间不共享容器,进一步强化模块边界。

5.2 Decorator Pattern:不改皮肤逻辑,动态叠加横切关注点

装饰器模式允许动态地为单个对象添加行为,而不影响同类的其他对象。它常与单一职责原则配合,将功能按关注点拆分为多个类。

术语卡用LoggingCommandHandlerDecorator作为示例。这个类在仓库五个模块中均有同名实现,例如 src/Modules/Meetings/Infrastructure/Configuration/Processing/LoggingCommandHandlerDecorator.cs:

internal class LoggingCommandHandlerDecorator<T> : ICommandHandler<T> where T : ICommand { private readonly ILogger _logger; private readonly IExecutionContextAccessor _executionContextAccessor; private readonly ICommandHandler<T> _decorated; public async Task Handle(T command, CancellationToken cancellationToken) { if (command is IRecurringCommand) { return await _decorated.Handle(command, cancellationToken); } using (LogContext.Push( new RequestLogEnricher(_executionContextAccessor), new CommandLogEnricher(command))) { try { this._logger.Information("Executing command {Command}", command.GetType().Name); var result = await _decorated.Handle(command, cancellationToken); this._logger.Information("Command {Command} processed successful", command.GetType().Name); return result; } catch (Exception exception) { this._logger.Error(exception, "Command {Command} processing failed", command.GetType().Name); throw; } } } }

装饰器的独特之处在于它"身兼两职":

  • 实现ICommandHandler<T>(即component角色);
  • 同时接受另一个ICommandHandler<T>实现(即concrete component角色),通常通过依赖注入传入。

从示例可以看到装饰器的典型价值:

  • IRecurringCommand(定时/内部命令)直接透传不做日志上下文包装;
  • 普通命令在执行前后记录开始/成功/失败日志;
  • CommandLogEnricher把命令 Id 写入日志上下文;
  • RequestLogEnricher把请求的CorrelationId(由 src/API/CompanyName.MyMeetings.API/Configuration/ExecutionContext/CorrelationMiddleware.cs 产生)写入日志上下文。

这样,命令处理链内任何一层产生的日志都自动携带命令 Id 与关联请求 Id,排查问题时可一键串联。

在 MyMeetings 中,命令处理管道是典型的装饰器链:LoggingCommandHandlerDecoratorUnitOfWorkCommandHandlerDecoratorDomainEventsDispatcherNotificationHandlerDecorator等依次包裹真实 Handler。每个装饰器只负责一个横切关注点(日志、事务、事件分发),互不干扰,正体现"把功能按关注点拆分"的设计思想。

注意:Decorator 极易与 Strategy 混淆。一句话区分——装饰器改变对象的"皮肤"(外层行为),策略改变对象的"内脏"(内部算法)

5.3 Strategy Pattern:运行时选择算法

策略模式(也称政策模式)是行为型设计模式,允许在运行时选择算法:代码不直接实现单一算法,而是接收运行时的指令来决定使用算法族中的哪一个。

策略模式有四个参与者:

  • Client(客户端):调用方代码;
  • Context(上下文):持有具体策略引用、与客户端交互的对象;
  • Strategy interface(策略接口):客户端通过 Context 在运行时设置具体策略所用的接口;
  • Concrete strategies(具体策略):策略接口的一个或多个实现。

MyMeetings 的定价子系统是策略模式的教科书式应用:

  • BuySubscriptionCommandHandler(客户端):通过PriceListFactory间接为PriceList设置当前策略;
  • PriceList(上下文):持有IPricingStrategy引用;
  • IPricingStrategy(策略接口):src/Modules/Payments/Domain/PriceListItems/PricingStrategies/IPricingStrategy.cs:
public interface IPricingStrategy { MoneyValue GetPrice(string countryCode, SubscriptionPeriod subscriptionPeriod, PriceListItemCategory category); }
  • 三个具体策略
    • DirectValueFromPriceListPricingStrategy:直接返回价目表价格(默认策略);
    • DiscountedValueFromPriceListPricingStrategy:在价目表价格上减去折扣额;
    • DirectValuePricingStrategy:直接返回固定值。

三者均位于 src/Modules/Payments/Domain/PriceListItems/PricingStrategies/。工厂中目前默认选用直接取价目表价格:

// 这是根据提供的数据与系统状态选择定价策略的地方。 IPricingStrategy pricingStrategy = new DirectValueFromPriceListPricingStrategy(priceListItems); return PriceList.Create(priceListItems, pricingStrategy);

来源:src/Modules/Payments/Application/PriceListItems/PriceListFactory.cs。

PriceListGetPrice在策略执行前先校验"该国家、周期、类别的价格必须已定义"(PriceForSubscriptionMustBeDefinedRule),保证策略算法基于合法数据运行。

一句话区分:策略让你改变对象的"内脏",装饰器让你改变"皮肤"。购买订阅这个用例同时也是多个模式组合的范例——Factory 负责创建PriceList,Strategy 负责定价算法。

六、从术语到测试:这些概念如何被验证

术语表不只是"名词解释",MyMeetings 为这些模式提供了对应的测试验证:

  • 单元测试:Meetings 模块在 src/Modules/Meetings/Tests/UnitTests/ 下针对MeetingGroupMeeting等领域对象编写了大量测试,覆盖聚合规则(如"只有付费小组才能组织会议");
  • 架构测试src/Tests/ArchTests/与各模块的ArchTests项目(如 src/Modules/Meetings/Tests/ArchTests/)用自动化测试守护模块边界,防止跨模块非法引用;
  • 集成测试src/Modules/Meetings/Tests/IntegrationTests/验证跨模块事件流程(如小组提案被接受后创建会议小组)的真实数据库行为。

术语卡中出现的 Act/Arrange/Assert、Given When Then、Mock、Stub、Integration Test、Unit Test 等条目,都可以在这三个测试层次中找到对应实现。这也解释了为什么术语表会把测试相关术语与 DDD 术语并列——模式的可信度来自测试的覆盖

七、尚待填充的术语与阅读建议

值得注意的是,术语表中部分条目目前只有标题(TODO 状态),包括:

  • Event-Driven Architecture(docs/catalog-of-terms/Event-Driven-Architecture/README.md)
  • Event Sourcing(docs/catalog-of-terms/Event-Sourcing/README.md)
  • Event Storming(docs/catalog-of-terms/Event-Storming/README.md)
  • Integration Event(docs/catalog-of-terms/Integration-Event/README.md)

阅读这些主题时,可以借助仓库中的其他资料补齐上下文:

  • Event Sourcing 的 Payload 结构、事件表设计与投影机制,可参考 docs/Images/ES_event_store_db_sample.png 与 docs/Images/ES_events_projection.png,以及 Payments 模块的AggregateStore(src/Modules/Payments/Infrastructure/AggregateStore/);
  • Event Storming 的工作坊产物,可参考 docs/Images/Payments_EventStorming_Design.jpg、docs/Images/User_Registration.jpg 等设计稿;
  • 集成事件的实际用法,可参考 src/Modules/Meetings/IntegrationEvents/ 与各模块的*IntegrationEventHandler.cs消费端。

八、总结:一份术语表如何撑起整套架构

回到docs/catalog-of-terms/README.md本身,这份索引的价值在于它把散落在整个代码库中的设计决策浓缩为一套统一的领域语言

层次关键术语仓库证据
战术模式Aggregate、Entity、Value ObjectMeetingGroup.cs、MoneyValue.cs
应用层Command、CQRS、Query、Read ModelCancelMeetingCommand.cs
事件Domain Event、Integration Event、Eventual ConsistencyDomainEventsDispatcher.cs、InMemoryEventBus.cs
模式Decorator、Strategy、Dependency Injection、FactoryLoggingCommandHandlerDecorator.cs、PriceListFactory.cs
工程化ADR、Architecture Test、Integration Test、CIdocs/architecture-decision-log/、src/Tests/ArchTests/

对于想要落地 DDD 的团队,这份术语表 + 源码的"双重学习路径"极具参考价值:先读术语卡片理解模式意图,再对照源码看真实实现,最后通过测试用例验证行为。以这样的方式学习,术语不再是抽象名词,而是可运行、可测试、可复用的工程实践。

【免费下载链接】modular-monolith-with-dddFull Modular Monolith application with Domain-Driven Design approach.项目地址: https://gitcode.com/GitHub_Trending/mo/modular-monolith-with-ddd

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/9/14 10:16:10

AI与区块链融合的内容审核系统设计与实践

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

作者头像 李华
网站建设 2026/9/14 10:13:21

电力系统碳排放流计算:基于Matlab的IEEE 14节点实现

1. 项目背景与核心价值电力系统碳排放流计算是当前能源低碳转型中的关键技术之一。这个项目复现了顶级EI期刊论文中提出的碳排放流计算方法&#xff0c;基于IEEE 14节点测试系统&#xff0c;使用Matlab实现了完整的计算流程。对于从事电力系统低碳运行、碳足迹追踪的研究人员和…

作者头像 李华
网站建设 2026/9/14 10:12:47

Mac mini + iPad + typec 数据线

1. 一直想找一种办法&#xff0c;把 iPad 当作 Mac mini 的显示器 2. 还真找到了&#xff0c;使用 duet 这个软件 iPad 和 Mac mini 同时安装 Mac mini 设置为开机自启动&#xff08;默认&#xff09; 3. 用一根 typec to typec 连接起来就可以了。无线有点延迟&#xff0c;但可…

作者头像 李华