抽象与建模能力详解:边界与契约
抽象与建模能力详解:边界与契约以 C# 上位机(工业设备监控/控制软件)为背景,用 DDD 思路讲清楚边界(Boundary)与契约(Contract),并给出可运行的完整代码。一、先给结论:什么是边界,什么是契约概念一句话定义解决的问题上位机中的体现边界把系统切成"谁负责什么"的范围划分复杂度隔离、团队分工、依赖方向设备通信层 / 业务逻辑层 / UI 层;核心域 / 支撑域 / 通用域契约边界之间交互的"约定",一旦定下不可随意改模块解耦、可替换、可测试接口IDeviceDriver、事件DeviceDataReceived、DTO 数据结构关键关系:先划边界 → 再定契约 → 契约是边界的"接口面"。二、边界:DDD 的三类子域 + 上位机的分层2.1 战略设计视角:核心域 / 支撑域 / 通用域以一个"多设备工业上位机"为例:上位机系统 ├── 核心域 (Core Domain) ← 公司的核心竞争力,要自研、要投入最强的人 │ └── 工艺配方引擎 / 报警决策引擎 / 数据采集调度算法 │ ├── 支撑域 (Supporting Domain) ← 业务需要但不核心,可自研也可外包 │ └── 设备管理 / 用户权限 / 报表 │ └── 通用域 (Generic Domain) ← 行业通用,直接买/用开源 └── 日志 / 串口通信 / 数据库ORM / 消息队列识别边界的三个提问法:这段代码改了会影响公司赚钱吗?→ 会 = 核心域这段代码换个项目还能用吗?→ 能 = 通用域这段代码是为某个业务场景定制的吗?→ 是 = 支撑域2.2 战术设计视角:限界上下文(Bounded Context)上位机常见的限界上下文:┌─────────────────────────────────────────────────────┐ │ 上位机应用 │ │ │ │ ┌──────────────┐ ┌──────────────┐ │ │ │ 设备接入上下文 │ │ 工艺执行上下文 │ │ │ │ (Device BC) │──▶│ (Recipe BC) │ │ │ └──────────────┘ └──────────────┘ │ │ │ │ │ │ ▼ ▼ │ │ ┌──────────────┐ ┌──────────────┐ │ │ │ 报警上下文 │ │ 数据持久化BC │ │ │ │ (Alarm BC) │ │ (Storage BC) │ │ │ └──────────────┘ └──────────────┘ │ └─────────────────────────────────────────────────────┘每个上下文内部:可以有自己的模型、自己的语言、自己的数据库。上下文之间:只能通过契约交互(不能直接引用对方的内部类)。2.3 上位机的物理分层边界(依赖方向)┌─────────────────────────────────────┐ │ UI 层 (WinForm/WPF) │ ← 只依赖 Application ├─────────────────────────────────────┤ │ Application 层 (用例编排) │ ← 只依赖 Domain + 抽象 ├─────────────────────────────────────┤ │ Domain 层 (核心域模型) │ ← 不依赖任何外部!纯 C# ├─────────────────────────────────────┤ │ Infrastructure 层 (串口/TCP/DB) │ ← 实现 Domain 定义的接口 └─────────────────────────────────────┘ ↑ 依赖倒置:Infra 依赖 Domain 的接口,而不是反过来边界铁律:Domain 层不能using System.IO.Ports。串口是基础设施,Domain 只认IDeviceChannel接口。三、契约:三种形态3.1 接口契约(Interface Contract)定义"我能做什么",隐藏"我怎么做的"。// 位于 Domain 层 —— 不依赖任何实现publicinterfaceIDeviceDriver{stringDeviceId{get;}TaskConnectResultConnectAsync(CancellationTokenct);TaskWriteResultWriteAsync(DeviceCommandcommand,CancellationTokenct);eventEventHandlerTelemetryReceivedEventArgsTelemetryReceived;}3.2 事件契约(Event Contract)定义"发生了什么",用于上下文之间异步解耦。// 领域事件:设备遥测到达(不包含"谁实现的"信息)publicsealedrecordTelemetryReceived(stringDeviceId,DateTimeOffsetTimestamp,IReadOnlyDictionarystring,doubleValues);3.3 数据结构契约(DTO / Value Object)定义"数据长什么样",跨边界传输。// 值对象:温度+单位,自校验publicreadonlyrecordstructTemperature{publicdoubleValue{get;}publicstringUnit{get;}publicTemperature(doublevalue,stringunit){if(unit!="C"unit!="F")thrownewArgumentException("非法单位");Value=value;Unit=unit;}}契约设计五原则:窄接口:一个接口只做一件事(IReader/IWriter分开)稳定:契约一旦发布,只能加不能改(向后兼容)无泄漏:契约里不出现实现细节(不暴露SerialPort类型)值语义:跨边界传值对象/DTO,不传实体引用显式失败:用ResultT而非抛异常跨边界四、完整代码案例:多设备采集上位机4.1 项目结构UpperComputer/ ├── UpperComputer.Domain/ # 核心域(无外部依赖) │ ├── Devices/ │ │ ├── IDeviceDriver.cs # 契约:设备驱动 │ │ ├── DeviceCommand.cs # 契约:命令 DTO │ │ ├── TelemetryReceived.cs # 契约:事件 │ │ └── DeviceId.cs # 值对象 │ ├── Alarms/ │ │ ├── IAlarmRule.cs # 契约:报警规则 │ │ └── AlarmEvaluator.cs # 核心域逻辑 │ └── Result.cs # 通用结果类型 │ ├── UpperComputer.Infrastructure/ # 基础设施(实现契约) │ ├── SerialDeviceDriver.cs # 实现 IDeviceDriver(串口) │ ├── TcpDeviceDriver.cs # 实现 IDeviceDriver(TCP) │ └── SqliteAlarmRepository.cs │ ├── UpperComputer.Application/ # 应用层(用例编排) │ └── TelemetryPipeline.cs │ └── UpperComputer.UI/ # WinForm 上位机界面 └── MainForm.cs4.2 Domain 层:定义契约(不依赖任何东西)// ============ Result.cs ============namespaceUpperComputer.Domain;publicreadonlyrecordstructResultT{publicboolIsSuccess{get;}publicT?Value{get;}publicstring?Error{get;}privateResult(boolok,T?value,string?error){IsSuccess=ok;Value=value;Error=error;}publicstaticResultTOk(Tvalue)=new(true,value,null);publicstaticResultTFail(stringerror)=new(false,default,error);}// ============ DeviceId.cs ============namespaceUpperComputer.Domain.Devices;publicreadonlyrecordstructDeviceId(stringValue){publicstaticDeviceIdParse(strings)=string.IsNullOrWhiteSpace(s)?thrownewArgumentException("DeviceId 不能为空"):new(s);}// ============ DeviceCommand.cs ============namespaceUpperComputer.Domain.Devices;/// summary契约:一条下发给设备的命令(值对象,不可变)/summarypublicsealedrecordDeviceCommand(stringOpCode,IReadOnlyDictionarystring,stringParameters){publicstaticDeviceCommandReadRegister(intaddress)=new("READ",newDictionarystring,string{["addr"]=address.ToString()});}// ============ TelemetryReceived.cs ============namespaceUpperComputer.Domain.Devices;/// summary契约:遥测事件(跨边界传值,不含实现)/summarypublicsealedrecordTelemetryReceived(DeviceIdDeviceId,DateTimeOffsetTimestamp,IReadOnlyDictionarys

相关新闻

MySQL 2013错误排查:从握手包到DNS反查的完整指南

MySQL 2013错误排查:从握手包到DNS反查的完整指南

简介:针对Navicat远程连接MySQL时报“2013-Lost connection to MYSQL server at waiting for initial communication packet”错误的场景,这份PDF文档完整梳理了从环境检验、配置文件修改到授权连接的操作链路,适合服务器运维人员、后端开发者…

2026/10/9 15:39:22 阅读更多 →
Ghidra 11.0.2 落地指南:从JDK 21配置到自动化分析脚本

Ghidra 11.0.2 落地指南:从JDK 21配置到自动化分析脚本

简介:Ghidra 11.0.2 是一款开源软件逆向工程框架,特别为 Linux 平台用户打包,适用于恶意代码分析、漏洞研究、协议逆向与 CTF 对抗等场景。该版本内置反汇编、反编译、绘图、脚本化等完整分析能力,支持多种处理器指令集和常见可执…

2026/10/9 15:39:22 阅读更多 →
LabVIEW 接 SQLite 实战:产线测控数据落库与查询避坑指南

LabVIEW 接 SQLite 实战:产线测控数据落库与查询避坑指南

简介:这份资源面向使用 LabVIEW 进行数据采集、测试测量与工控上位机开发的工程师,以及需要为程序加入本地数据持久化能力的学习者,重点解决 LabVIEW 连接数据库时建库繁琐、缓存难清理、编程门槛高的问题。压缩包共 148 个文件,约…

2026/10/9 15:39:22 阅读更多 →

最新新闻

Cline 实战踩坑实录:Token 烧钱、权限误伤、上下文爆炸,这三座大山怎么翻?

Cline 实战踩坑实录:Token 烧钱、权限误伤、上下文爆炸,这三座大山怎么翻?

Cline 实战踩坑实录:Token 烧钱、权限误伤、上下文爆炸,这三座大山怎么翻? 【免费下载链接】cline Autonomous coding agent as an SDK, IDE extension, or CLI assistant. 项目地址: https://gitcode.com/GitHub_Trending/cl/cline 开…

2026/10/10 17:57:27 阅读更多 →
AI 时代还需要传统搜索引擎吗?Hister 的 MCP 集成给出了另一种答案

AI 时代还需要传统搜索引擎吗?Hister 的 MCP 集成给出了另一种答案

AI 时代还需要传统搜索引擎吗?Hister 的 MCP 集成给出了另一种答案 【免费下载链接】hister Your own search engine 项目地址: https://gitcode.com/GitHub_Trending/hi/hister ChatGPT 式 AI 搜索的爆发,让一个原本不成问题的问题重新摆上台面&…

2026/10/10 17:57:27 阅读更多 →
iwe新手完全指南:5分钟安装、初始化并搜索你的第一篇笔记

iwe新手完全指南:5分钟安装、初始化并搜索你的第一篇笔记

人工智能Agent 记忆MCP 服务CLI知识管理开发工具 【免费下载链接】iwe Markdown knowledge graph — LSP for your editor, CLI MCP memory for your AI agents 项目地址: https://gitcode.com/gh_mirrors/iw/iwe 点击查看 免费下载 iwe 是一款开源的 Markdown 知…

2026/10/10 17:56:26 阅读更多 →
python类的私有属性和公共属性说明

python类的私有属性和公共属性说明

前言 「Python 的私有属性」这个说法,本身就不太准确。官方教程里写得很干脆:在 Python 中,那种「除非在对象内部,否则无法访问」的私有实例变量是不存在的。 之所以大家还总把它挂在嘴边,是因为 Python 提供了两套约定…

2026/10/10 17:56:26 阅读更多 →
关于数据规范的教训

关于数据规范的教训

1、背景今年开始继续维护之前的数据平台,最近维护是2025年前半年,当时另外还有俩同事。维护的过程中遇到了数据显示错误的bug,具体来说,就是因业务要求,需要对每一条数据标记归属。默认归属是线下,可以标记…

2026/10/10 17:56:26 阅读更多 →
软件评审检查表:从需求到测试的逐项评审实践指南

软件评审检查表:从需求到测试的逐项评审实践指南

简介:这是一份面向软件设计与开发评审场景的实用检查表文档,适合项目经理、架构师、开发人员和质量管理人员使用。文档将评审过程拆解为需求规格说明书检查、概要设计检查和详细设计检查三大模块,覆盖清晰性、完整性、依从性、一致性、可行性…

2026/10/10 17:56:25 阅读更多 →

日新闻

卫星轨道分类全解析:从LEO到GEO的选型逻辑与工程实践

卫星轨道分类全解析:从LEO到GEO的选型逻辑与工程实践

1. 从“卫星轨道分类”这个标题说起:为什么值得花时间搞懂第一次接触“卫星轨道分类”这个概念,很多人会觉得它离自己很远——不就是天上的星星怎么转吗?但如果你正在做航天任务规划、遥感数据接收、星座设计,甚至只是准备一场航天…

2026/10/10 0:00:39 阅读更多 →
Spring AOP 核心原理与实战:从概念到日志切面落地

Spring AOP 核心原理与实战:从概念到日志切面落地

1. 从一个真实痛点说起:为什么你的代码里到处都是重复逻辑刚入行那会儿,我写过一个用户管理模块,注册、登录、改密码、注销四个接口。每个接口里都塞了几乎一样的日志打印、参数校验、事务开启和提交。当时觉得没什么,能跑就行。直…

2026/10/10 0:00:40 阅读更多 →
Python招聘数据采集与分析可视化:从采集清洗到薪资技能城市可视化全链路

Python招聘数据采集与分析可视化:从采集清洗到薪资技能城市可视化全链路

简介:这是一套面向计算机相关专业学生与项目实战学习者的Python数据采集与分析可视化完整项目,以Boss直聘岗位数据为对象,适合用作毕业设计、课程设计或期末大作业。资源包共38个文件,约246KB,以13个py源码文件为核心&…

2026/10/10 0:00:40 阅读更多 →

周新闻

KT148A语音芯片外挂8002D功放的工程实践指南

KT148A语音芯片外挂8002D功放的工程实践指南

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

2026/10/10 11:14:25 阅读更多 →
LLC谐振变换器增益公式推导:从FHA等效到完整归一化表达式

LLC谐振变换器增益公式推导:从FHA等效到完整归一化表达式

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

2026/10/10 1:36:08 阅读更多 →
ARM架构深度解析:从RISC设计理念到交叉编译实战

ARM架构深度解析:从RISC设计理念到交叉编译实战

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

2026/10/10 11:14:58 阅读更多 →

月新闻

我发现了一个新思路:用 Remotion + Claude Code 像写代码一样自动化生成短视频

我发现了一个新思路:用 Remotion + Claude Code 像写代码一样自动化生成短视频

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

2026/10/10 5:23:50 阅读更多 →
Windows下 Codex 中 Chrome 和 Computer Use 插件不可用问题排查及解决参考方式:TaoToken 统一 Key 配置与验证

Windows下 Codex 中 Chrome 和 Computer Use 插件不可用问题排查及解决参考方式:TaoToken 统一 Key 配置与验证

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

2026/10/9 21:32:20 阅读更多 →
黑夜航拍船只数据集训练YOLOV5模型全流程解析

黑夜航拍船只数据集训练YOLOV5模型全流程解析

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

2026/10/10 10:38:42 阅读更多 →