“事件风暴 → 上下文映射 → 模块化”在 ABP vNext 的全链路模板 - 详解

news/2025/11/7 12:57:47/文章来源:https://www.cnblogs.com/yangykaifa/p/19199340

“事件风暴 → 上下文映射 → 模块化”在 ABP vNext 的全链路模板 ✨



0) 摘要(TL;DR)

本文交付一套从业务共创工作坊可运行工程骨架的闭环:
事件风暴 → 子域/上下文划分 → 上下文映射(关系/协作模式) → ABP 模块边界与依赖矩阵契约门禁(CI)伴随测试分阶段迁移老系统持续度量与反模式清单

总览图(从白板到上线)

事件风暴
Event Storming
上下文映射
Context Mapping
ABP 模块边界
七层分离 & DependsOn
契约清单
HTTP OpenAPI / 消息 Schema
️ CI 门禁
oasdiff / ArchRules / Pact
伴随测试
单元/集成/Testcontainers
渐进迁移
Strangler Fig
可观测 & 度量
耦合/破坏率/SLO

1) 工作坊与产出物

1.1 事件风暴(Event Storming)

  • 自上而下:Big PictureProcess/Design level
  • 把“命令 → 领域事件 → 聚合 → 读模型”排成时间线,沉淀统一语言(UL),形成“能力清单”。

仓库产出模板

/docs/event-storming/board.md          # 事件清单/照片转录
/docs/event-storming/glossary.yaml     # 统一语言词典
/docs/event-storming/capabilities.csv  # 能力项(为切上下文/模块做输入)

1.2 从事件风暴到上下文映射(Context Mapping)

  • 常见关系:Customer–Supplier、Conformist、ACL、Open Host、Published Language、Shared Kernel
  • 明确上游/下游、治理关系、语义边界与演进策略。

上下文映射示意

Downstream
Upstream
API/PL
翻译/对齐
ACL 防腐层
Sales
Conformist
Catalog
OHS + Published Language

2) 映射到 ABP 模块边界(工程化落地)️

2.1 模块命名与分层(建议 7 层)

约定命名:Company.Product.<Context>.*。每个上下文建议包含:

  • Domain.Shared / Domain
  • Application.Contracts / Application
  • HttpApi / HttpApi.Client
  • EntityFrameworkCore(或 MongoDB

依赖方向(只允许“向内”)

Web/UI
HttpApi
Application.Contracts
Application
Domain
EntityFrameworkCore

关键约束:

  • HttpApi仅依赖Application.Contracts(不依 Application 实现)。
  • HttpApi.Client仅依赖Application.Contracts
  • ORM 集成层仅依赖 Domain
  • 严禁跨上下文直连仓储,一律通过 契约(HTTP/消息)

2.2 ABP CLI:创建解决方案与模块

安装/更新 CLI

dotnet tool install -g Volo.Abp.Studio.Cli
# 或
dotnet tool update -g Volo.Abp.Studio.Cli

新建解决方案(MVC 示例)

abp new Contoso.SalesSuite -t app -u mvc

为上下文创建 DDD 模块并加入解决方案Studio CLI 前缀:abpc):

cd Contoso.SalesSuite
abpc new-module Contoso.Sales   -t module:ddd -ts Contoso.SalesSuite.sln
abpc new-module Contoso.Billing -t module:ddd -ts Contoso.SalesSuite.sln
abpc new-module Contoso.Catalog -t module:ddd -ts Contoso.SalesSuite.sln

3) 上下文通信:HttpApi.Client 动态代理 + Polly(重试/熔断)

端点配置(消费者侧 appsettings.json

{
"RemoteServices": {
"Default": { "BaseUrl": "https://localhost:5001/" },
"Billing": { "BaseUrl": "https://localhost:6001/" }
}
}

注册动态代理 + Polly(关键扩展点 ProxyClientBuildActions

[DependsOn(typeof(AbpHttpClientModule),
typeof(Contoso.Billing.ApplicationContractsModule))]
public class Contoso.BillingClientModule : AbpModule
{
public override void PreConfigureServices(ServiceConfigurationContext context)
{
PreConfigure<AbpHttpClientBuilderOptions>(options =>{options.ProxyClientBuildActions.Add((remoteServiceName, clientBuilder) =>{var jitter = new Random();clientBuilder.AddTransientHttpErrorPolicy(pb =>pb.WaitAndRetryAsync(3, i =>TimeSpan.FromSeconds(Math.Pow(2, i)) +TimeSpan.FromMilliseconds(jitter.Next(0, 150))));});});}public override void ConfigureServices(ServiceConfigurationContext context){context.Services.AddHttpClientProxies(typeof(Contoso.Billing.ApplicationContractsModule).Assembly,remoteServiceConfigurationName: "Billing");}}

调用时序图

Sales(Consumer)HttpApi.Client ProxyHTTPBilling(HttpApi)调用 IInvoiceAppService.Get("123")HTTP GET /api/invoices/123转发至 Controller200 OK / JSONopt[Retry(指数退避 +抖动 3 次)]DTOSales(Consumer)HttpApi.Client ProxyHTTPBilling(HttpApi)

4) 一致性:分布式事件总线 + Outbox/Inbox(EF Core)

DbContext 接线(最小示例)

public class SalesDbContext : AbpDbContext<SalesDbContext>, IHasEventOutbox, IHasEventInbox{public DbSet<OutgoingEventRecord> OutgoingEvents { get; set; }public DbSet<IncomingEventRecord> IncomingEvents { get; set; }protected override void OnModelCreating(ModelBuilder builder){base.OnModelCreating(builder);builder.ConfigureEventOutbox();builder.ConfigureEventInbox();}}

模块中绑定 Outbox/Inbox 到事件总线

public class SalesEntityFrameworkCoreModule : AbpModule
{
public override void ConfigureServices(ServiceConfigurationContext context)
{
Configure<AbpDistributedEventBusOptions>(o =>{o.Outboxes.Configure(c => c.UseDbContext<SalesDbContext>());o.Inboxes.Configure(c  => c.UseDbContext<SalesDbContext>());});}}

处理流示意

应用服务
Save + Publish
本地事务
业务表
Outbox(OutgoingEvents)
Outbox Processor
消息中间件
Consumer Service
Inbox(IncomingEvents)
幂等处理
业务逻辑

多实例需配置分布式锁(如 Redis)防止重复并发处理;事件载荷中建议携带 TenantId,消费端使用 using (CurrentTenant.Change(...)) 切换。


5) 多租户与数据边界

服务侧按租户执行

public class SalesReportAppService : ApplicationService
{
private readonly IRepository<Order, Guid> _orders;public SalesReportAppService(IRepository<Order, Guid> orders) => _orders = orders;public async Task<long> CountOrdersAsync(Guid tenantId){using (CurrentTenant.Change(tenantId)){return await _orders.GetCountAsync();}}}

原则:上下文内统一通过 ICurrentTenant 获取租户,严禁跨上下文“越界”读写他域租户数据。


6) 契约门禁(流水线守门员)️

6.1 API 契约:Swashbuckle CLI + oasdiff(检测破坏性变更)

生成 OpenAPI(构建后导出)

dotnet tool install --global Swashbuckle.AspNetCore.Cli
dotnet swagger tofile --output ./artifacts/api.v1.json \
./src/Contoso.Sales.HttpApi.Host/bin/Release/net8.0/Contoso.Sales.HttpApi.Host.dll v1

CI 检查(GitHub Actions 片段)

- name: Check OpenAPI breaking changes
uses: Tufin/oasdiff-action@v2.1.3
with:
base: './contracts/api/sales.v1.json'
revision: './artifacts/api.v1.json'
check-breaking: true
fail-on-diff: true

6.2 架构门禁:NetArchTest / ArchUnitNET

using NetArchTest.Rules;
using Xunit;
public class ArchitectureTests
{
[Fact]
public void Catalog_Should_Not_Depend_On_Billing_EFCore()
{
var result = Types.InAssemblies(AppDomain.CurrentDomain.GetAssemblies())
.That().ResideInNamespace("Contoso.Catalog", true)
.ShouldNot().HaveDependencyOn("Contoso.Billing.EntityFrameworkCore")
.GetResult();
Assert.True(result.IsSuccessful, string.Join("\n", result.FailingTypeNames));
}
}

CI 编排图

无破坏
通过
通过
有破坏
违规
失败
开发者提交 PR
构建/测试
生成 OpenAPI
oasdiff breaking
️ NetArchTest 规则
PactNet 合同测试
✅ 合并
⛔ 退回并评论

7) 伴随测试(从用例到回归)

7.1 契约测试(PactNet v4 Consumer 示例)——已换为 .WithHttpInteractions()

var pact = Pact.V4("SalesConsumer", "BillingProvider", new PactConfig { PactDir = "../../../pacts" })
.WithHttpInteractions();
pact.UponReceiving("get invoice")
.WithRequest(HttpMethod.Get, "/api/invoices/123")
.WillRespond()
.WithStatus(HttpStatusCode.OK)
.WithJsonBody(new { id = Match.Type("123"), amount = Match.Decimal(10.5) });
await pact.VerifyAsync(async ctx => {
var client = new HttpClient { BaseAddress = ctx.MockServerUri };
var res = await client.GetAsync("/api/invoices/123");
res.EnsureSuccessStatusCode();
});

7.2 组件/集成:Testcontainers(PostgreSQL/RabbitMQ)

带等待策略与测试集合夹具(复用容器,提速 & 稳定)

[CollectionDefinition("integration-shared")]
public class IntegrationSharedCollection : ICollectionFixture<SharedContainers> { }public class SharedContainers : IAsyncLifetime{public PostgreSqlContainer Pg { get; private set; } = null!;public RabbitMqContainer Mq { get; private set; } = null!;public async Task InitializeAsync(){Pg = new PostgreSqlBuilder().WithImage("postgres:16-alpine").Build();Mq = new RabbitMqBuilder().WithImage("rabbitmq:3-management-alpine").Build();await Pg.StartAsync();await Mq.StartAsync();}public async Task DisposeAsync(){await Mq.DisposeAsync();await Pg.DisposeAsync();}public string Db => Pg.GetConnectionString();public string Amqp => Mq.GetConnectionString();}

8) 分阶段迁移老系统(Strangler Fig)

Phase 0 盘点
域/能力/耦合图谱
️ Phase 1 围栏
ACL + 只读镜像
Phase 2 影子/双写
对账+差异报表门禁
Phase 3 切换
分段接管/回退剧本
发布复盘
破坏率/回滚率/改进项

反模式红线:共享数据库、跨上下文事务、DTO 当领域模型复用、HttpApi 依赖 Application 实现、跨境引用 *.EntityFrameworkCore


9) 可观测与演进度量


10) 工程骨架(落地目录)️

/src/Contoso.Sales.Domain.Shared/Contoso.Sales.Domain/Contoso.Sales.Application.Contracts/Contoso.Sales.Application/Contoso.Sales.HttpApi/Contoso.Sales.HttpApi.Client/Contoso.Sales.EntityFrameworkCore/Contoso.Billing.(同上)/Contoso.Catalog.(同上)
/contracts/api/sales.v1.json/api/billing.v1.json/messages/.schema.json
/quality-gates/ApiCompat          # oasdiff 产物与基线/ArchRules.Tests    # 架构规则测试
/tests/Sales.AcceptanceTests/Sales.ComponentTests
/docs/event-storming/*/context-map/*

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/958736.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

LRU缓存科普与实现(Kotlin 与 Swift) - 实践

pre { white-space: pre !important; word-wrap: normal !important; overflow-x: auto !important; display: block !important; font-family: "Consolas", "Monaco", "Courier New", …

十六、Linux网络基础理论 - OSI模型、TCP/IP协议与IP地址详解 - 教程

十六、Linux网络基础理论 - OSI模型、TCP/IP协议与IP地址详解 - 教程pre { white-space: pre !important; word-wrap: normal !important; overflow-x: auto !important; display: block !important; font-family: &qu…

Physicians High School Chemistry

Its not physical chemistry. First

深入解析:【Unity】uNet游戏服务端框架(一)服务端架构设计

pre { white-space: pre !important; word-wrap: normal !important; overflow-x: auto !important; display: block !important; font-family: "Consolas", "Monaco", "Courier New", …

Python哈希机制深度解析:从底层原理到工程实践

Python哈希机制深度解析:从底层原理到工程实践 摘要:本文系统剖析Python哈希机制的实现原理、冲突解决策略及数据类型适配规则,通过理论溯源、源码分析与多维度实验验证,揭示其在字典、集合等核心数据结构中的底层…

LaTeX学习笔记:数学公式编辑

LaTeX学习笔记:数学公式编辑LaTeX学习笔记:数学公式编辑 数学公式的编辑一直以来都是我们在撰写科学类文章过程中所面临的最大麻烦之一,相信大家之前应该都尝试过各种在电子文档中输入数学公式的方法,但这些方法多…

容器未正确挂载指定目录 (通常与 目录不存在、权限不足、路径拼写错误 或 系统安全策)

容器未正确挂载指定目录 (通常与 目录不存在、权限不足、路径拼写错误 或 系统安全策) 第一个参数: -v 特别注意⚠️: 以上 -v ~/zapas/dbdata/mssql:/var/opt/mssql/data ~ 等于 /home/当前用户目录 即等于…

飞牛OS Root用户SSH公钥登录完整教程

飞牛OS Root用户SSH公钥登录完整教程 前言 飞牛OS(fnOS)是一款基于Debian的NAS操作系统,提供了强大的数据存储和管理功能。在日常使用和管理过程中,SSH远程登录是必不可少的重要工具。然而,出于安全考 虑,飞牛OS…

红黑树简

package J_TreeSet;import java.util.Objects;public class B_Student implements Comparable<B_Student>{private String name;private int age;public B_Student() {}public B_Student(String name, int age) …

⸢ 柒-Ⅲ⸥⤳ 可信纵深防御建设方案:数据使用可信端安全可信 - 详解

pre { white-space: pre !important; word-wrap: normal !important; overflow-x: auto !important; display: block !important; font-family: "Consolas", "Monaco", "Courier New", …

【日记】我从来没见过有酒店这么设计的(533 字)

正文出差结束,终于回家了。回到自己的小房间之后,仔仔细细想了一下,发现自己有好多事情都没做。屯了好多经济学的文章没有看、内科学的笔记、买菜、写文章(日记、学海计划、实用类随记、计算机指南,今年的年度总结…

第181-182天:横向移动篇PTH哈希PTT票据PTK密匙Kerberoast攻击点TGTNTLM爆破

首要知识点: pass the hash(哈希传递攻击,简称pth) pass the ticket(票据传递攻击,简称ptt) pass the key(密钥传递攻击,简称ptk) PTH(pass the hash) #利用的lm或ntlm的值进行的渗透测试(NTLM认证攻击)…

2025年上海装修设计标杆公司最新推荐:中古风装修/轻法式装修/现代简约装修/极简风装修、上海千祥建筑定义品质居住新标准

随着城市化进程深化及居民对居住品质要求的提升,家装行业正经历从基础施工向设计美学、环保健康与全程服务一体化的转型。2025年,装修需求预计进一步释放,但市场上装修公司设计水平、施工质量、材料环保标准及售后服…

AI在开源情报搜集系统中的应用汇总

大模型在开源情报系统中的应用已贯穿采集、处理、分析、决策全链条,形成了一个从“数据获取”到“认知生成”的智能闭环。上述功能体系不仅提升了情报工作的效率与深度,更推动了情报模式从“被动检索”向“主动洞察”…

清理docker的overlay2目录

使用Docker时,镜像和容器数据都存储在Docker的存储目录中,默认是/var/lib/docker。在Docker使用overlay2存储驱动时,/var/lib/docker/overlay2目录包含了overlay2存储驱动所使用的文件和目录。 overlay2是Docker的一…

升鲜宝生鲜配送供应链管理系统---PMS--商品品牌多语言存储与 Redis 缓存同步实现

升鲜宝生鲜配送供应链管理系统---PMS--商品品牌多语言存储与 Redis 缓存同步实现 商品品牌多语言存储与 Redis 缓存同步实现文档 本设计文档说明商品品牌(pms_brand)在支持多语言环境下的数据存储、翻译同步及 Redis…

网站在苹果 Safari 进行适配遇到的问题

在网站进行移动端 Web 适配开发中,弹窗和导航栏弹出等常常会出现一些问题,如果是奇奇怪怪的客户严格要求的话,那么就会有下面这些情况:打开弹窗后页面自动放大,视图区被放大到看不全 打开对话框打开后背景仍然能滚…

Python对象模型的认知陷阱:类的`__name__`属性与名字绑定的本质辨析

Python对象模型的认知陷阱:类的__name__属性与名字绑定的本质辨析 摘要:本文通过一个典型的元类使用错误,深入剖析Python对象模型中“类的名称属性”与“名字绑定”这两个常被混淆的核心概念。许多开发者在动态创建…

Python环境教程(三)-环境高级之uv pixi

UV 官网:uv 中文文档 Github地址:astral-sh/uv: An extremely fast Python package and project manager, written in Rust. 什么是uv? uv 是由 Astral 公司开发的一款 Rust 编写的 Python 包管理器和环境管理器,它…

升鲜宝生鲜配送供应链管理系统---PMS 商品模块 + 动态翻译设计说明

PMS 商品模块 + 动态翻译设计说明书 一、模块总体定位 PMS(Product Master System)是供应链体系的商品主数据中心,负责统一维护商品的品牌、分类、标签、单位、材质、存储方式、分拣区域、SPU、SKU 及多语言翻译。 …