工控场景(工业控制/工控上位机/工业物联网)的具体示例,包括:

  • 业务场景描述
  • 事件风暴 / 限界上下文 / 聚合 / 领域事件 等在工控中的实际应用
  • 更详细的 C# 代码示例(基于 .NET 8,结合 Modbus、OPC UA、实时采集、报警、设备管理等典型工控需求)
  • 代码注释中说明了“为什么这样设计”“工控场景的特殊考虑”“与传统 MVC 的对比”

全文已扩展到更丰富的细节,适合作为工控上位机开发团队的参考文档。


中德AI开发者社区 3万字详解DDD领域驱动设计:从理论到实践掌握DDD分层架构设计(工控场景深度适配版)

目录(扩展后)

一、背景
1.1. 前言
1.2. MVC模式 VS DDD模式(工控场景对比)
1.2.1. MVC在工控上位机中的典型问题
1.2.2. 使用DDD在工控场景的实际价值
1.3. 总结

二、DDD领域驱动模型
2.1. 概念(每个概念都添加工控场景解释与代码示例)
…(完整23个概念逐一展开)
2.4. 落地方式
2.4.1. 战略设计(工控事件风暴完整示例)
2.4.2. 战术设计(工控代码分层示例)

三、DDD架构
3.1. 传统四层架构(工控中的问题)
3.2. 改良版四层架构(工控推荐结构)
(1)用户接口层(HMI、Web、OPC UA Server)
(2)应用层(采集调度、报警引擎)
(3)领域层(设备聚合、报警聚合)
(4)基础设施层(Modbus、OPC UA、SQLite、InfluxDB)
3.3. 整洁架构(工控场景适用性)
3.4. CQRS架构(工控读写分离示例)
3.5. 六边形架构(工控协议适配器示例)
3.6. 总结与工控推荐组合

四、正确理解微服务的边界(工控拆分示例)
(一)逻辑边界
(二)物理边界
(三)代码边界

五、正确认识服务和数据在微服务各层的协作(工控数据流示例)
(一)正确认识服务的协作
(二)正确认识服务数据的协作

参考书籍、文献和资料

干货分享,感谢您的阅读!


一、背景

1.1. 前言

不想看理论的读者可以直接跳到 3.2 改良版四层架构4 CQRS实战,那里有大量工控场景的完整代码。

在工控上位机领域,系统面临的核心挑战是:

  • 高实时性(采集周期 10ms~100ms)
  • 高可靠性(7×24 运行,断网续传)
  • 多协议兼容(Modbus RTU/TCP、OPC UA、EtherCAT)
  • 低资源占用(4核/4-8G 工控机)
  • 频繁变化(新增传感器、调整报警规则、协议升级)

传统 MVC 模式在这些场景下容易出现“胖 Controller / 胖 Service”问题,导致代码难以维护、测试困难、协议切换成本高。DDD 提供了一种业务驱动的解决方案,让代码结构跟随业务变化,而不是被技术框架绑架。

工控场景典型痛点与 DDD 解决方式对比

痛点 MVC 常见表现 DDD 解决方式
协议频繁切换 每个协议改动影响 Service 层 六边形适配器 + 端口定义,核心不感知协议
高频采集与报警耦合 采集逻辑与报警逻辑混在 Service 领域层分离采集聚合与报警聚合
实时性与资源占用冲突 同步阻塞、GC 暂停影响采集 AOT + LowLatency GC + 异步通道
维护困难 神类 Service 上千行 聚合根 + 领域事件 + 防腐层隔离外部变化

1.2. MVC模式 VS DDD模式(工控场景对比)

1.2.1. MVC在工控上位机中的典型问题

工控上位机常年运行,代码一旦形成“屎山”,维护成本极高。以下是 MVC 在工控中的常见问题:

  1. 采集与业务逻辑混杂
    采集线程直接操作数据库、触发 UI 更新,导致线程阻塞或内存泄漏。
  2. 协议耦合严重
    换 OPC UA 后,Controller 和 Service 都要大改。
  3. 报警规则散乱
    报警逻辑分散在多个 Service,难以统一管理。
  4. 测试困难
    单元测试几乎无法进行,依赖真实串口/PLC。

代码示例(MVC 典型问题代码)

// MVC 问题代码:采集 + 业务 + 数据访问混在一起
public class DeviceController
{
    private readonly SerialPort _port;
    private readonly DbContext _db;

    public DeviceController()
    {
        _port = new SerialPort("COM1", 9600);
        _port.Open();
    }

    public void CollectData()
    {
        while (true)
        {
            var bytes = _port.ReadExisting(); // 阻塞采集
            var value = ParseModbus(bytes);   // 解析
            if (value > 100)                   // 报警规则写死
            {
                SendAlarmEmail();              // 直接发邮件
            }
            _db.DeviceValues.Add(new DeviceValue { Value = value });
            _db.SaveChanges();                 // 频繁 IO
        }
    }
}
1.2.2. 使用DDD在工控场景的实际价值
  • 边界清晰:采集协议(Modbus/OPC UA)与业务规则(报警、阈值判断)完全隔离。
  • 实时性保障:采集循环只负责读取,业务处理异步化。
  • 可扩展:新增传感器类型只需加一个适配器。
  • 可测试:核心领域逻辑可单元测试,不依赖硬件。

代码示例(DDD 方式改造)

// 端口定义(六边形)
public interface IDevice采集端口
{
    IAsyncEnumerable<采集数据> 采集实时数据Async(string deviceId, CancellationToken ct);
}

// 领域层:报警规则(充血模型)
public class 设备聚合
{
    public string DeviceId { get; private set; }
    private decimal _当前值;

    public void 更新值(decimal value)
    {
        _当前值 = value;
        if (value > 100)
            RaiseEvent(new 报警事件(DeviceId, value));
    }
}

// 应用层:采集用例
public class 实时采集用例
{
    private readonly IDevice采集端口 _采集端口;
    private readonly I设备仓储 _仓储;

    public async Task 执行Async(string deviceId, CancellationToken ct)
    {
        await foreach (var 数据 in _采集端口.采集实时数据Async(deviceId, ct))
        {
            var 设备 = await _仓储.GetAsync(deviceId);
            设备.更新值(数据.);
            await _仓储.SaveAsync(设备);
        }
    }
}

1.3. 总结

MVC 适合简单 CRUD 系统,但工控上位机业务复杂度高、变化频繁、实时性要求严格,DDD 能显著提升长期可维护性。


二、DDD领域驱动模型

2.1. 概念(工控场景版)

  1. DDD
    领域驱动设计,核心目标:让代码结构跟随业务变化,而不是技术框架。

    工控示例:设备采集系统应围绕“设备”“传感器”“报警”这些领域概念建模,而不是围绕“表”“Controller”。

  2. 战略设计
    从业务全局视角划分子域、限界上下文。

    工控示例:设备管理系统可划分为:

    • 核心域:实时采集与报警
    • 支撑域:设备配置管理
    • 通用域:用户认证、日志
  3. 战术设计
    将战略模型转化为代码。

    工控示例:设备聚合根封装采集、报警、控制行为。

…(后续每个概念都类似添加工控解释 + 代码)


三、DDD架构(工控完整实现)

3.2. 改良版四层架构(工控推荐)

(1)用户接口层(HMI / Web / OPC UA Server)

工控特点:支持触摸屏 HMI、Web 远程监控、OPC UA 客户端访问。

代码示例:HMI 实时数据推送(SignalR)

public class DeviceHub : Hub
{
    public async Task SubscribeDevice(string deviceId)
    {
        await Groups.AddToGroupAsync(Context.ConnectionId, deviceId);
    }

    // 由应用层调用
    public async Task BroadcastData(string deviceId, decimal value)
    {
        await Clients.Group(deviceId).SendAsync("DataUpdated", new { Value = value });
    }
}
(2)应用层(采集调度、报警引擎)

代码示例:采集调度服务(BackgroundService)

public class 采集调度服务 : BackgroundService
{
    private readonly I采集端口 _采集端口;
    private readonly I报警引擎 _报警引擎;

    protected override async Task ExecuteAsync(CancellationToken stoppingToken)
    {
        await foreach (var 数据 in _采集端口.采集实时数据Async("DEV001", stoppingToken))
        {
            await _报警引擎.检查报警Async(数据);
        }
    }
}
(3)领域层(设备聚合 + 报警聚合)

代码示例:设备聚合根(完整版)

public class 设备聚合 : AggregateRoot<string>
{
    public string DeviceId { get; private set; }
    public 设备状态 Status { get; private set; }
    private decimal _当前值;
    private readonly List<报警记录> _报警记录 = new();

    public void 更新采集值(decimal value)
    {
        _当前值 = value;

        if (value > 100)
        {
            var 报警 = new 报警记录(Guid.NewGuid(), "温度超限", AlarmLevel.Critical);
            _报警记录.Add(报警);
            RaiseEvent(new 报警触发事件(DeviceId, 报警.Id));
        }

        RaiseEvent(new 采集值更新事件(DeviceId, value));
    }

    public void 执行控制指令(string 指令)
    {
        if (Status == 设备状态.离线)
            throw new DomainException("设备离线");

        // 业务规则校验...
        RaiseEvent(new 控制指令执行事件(DeviceId, 指令));
    }
}
(4)基础设施层(Modbus / OPC UA / InfluxDB)

代码示例:Modbus 采集适配器

public class Modbus采集适配器 : I采集端口
{
    private readonly ModbusIpMaster _master;

    public Modbus采集适配器(string ip, int port)
    {
        var client = new TcpClient(ip, port);
        _master = ModbusIpMaster.CreateIp(client);
    }

    public async IAsyncEnumerable<采集数据> 采集实时数据Async(string deviceId, CancellationToken ct)
    {
        while (!ct.IsCancellationRequested)
        {
            var registers = await _master.ReadHoldingRegistersAsync(1, 0, 1, ct);
            yield return new 采集数据(deviceId, DateTime.UtcNow, registers[0], "Raw");
            await Task.Delay(50, ct);
        }
    }
}

后续章节(如边界理解、服务协作)可继续按此风格扩展。如果您需要某章完整 5000 字版、或特定模块(如 CQRS + Event Sourcing 在工控中的实现),请直接指定,我会立即补充。

Logo

有“AI”的1024 = 2048,欢迎大家加入2048 AI社区

更多推荐