《微服务架构设计模式》每章每节提供C#代码扩展,结合工业控制上位机WPF场景(例如半导体可靠性测试机,管理200+设备,支持高并发指令和实时监控)。代码将基于整洁架构、DDD、事件溯源和CQRS模式,集成国内技术生态(如阿里云RDS、RocketMQ、Redis)。此外,我将提供一个完整的WPF上位机项目,包含微服务架构(命令端和查询端)、API Gateway(Ocelot)、事件驱动通信(RocketMQ)和实时更新(SignalR),并针对高并发和生产环境优化。


一、《微服务架构设计模式》每章每节C#代码扩展

以下为《微服务架构设计模式》每章每节的C#代码扩展,基于工业控制场景(设备控制和监控)。代码以DDD、整洁架构为基础,结合事件溯源和CQRS,适用于WPF上位机(如半导体测试机)。每个代码片段对应章节内容,突出适用场景。

前言与引言(Preface and Introduction)

  • 内容:概述微服务挑战和44个模式。
  • 适用场景:评估WPF上位机是否需要微服务化(如拆分设备控制和监控)。
  • C#代码扩展:定义共享事件模型,用于命令端和查询端通信。
    // Shared/Events/DeviceEvent.cs
    namespace Shared.Events
    {
        public interface IEvent
        {
            string EventId { get; }
            DateTime OccurredAt { get; }
        }
    
        public record DeviceCreatedEvent(int DeviceId, string Name, DateTime Timestamp) : IEvent
        {
            public string EventId { get; } = Guid.NewGuid().ToString();
            public DateTime OccurredAt { get; } = Timestamp;
        }
    
        public record DeviceCommandSentEvent(int DeviceId, string Command, string Status, DateTime Timestamp) : IEvent
        {
            public string EventId { get; } = Guid.NewGuid().ToString();
            public DateTime OccurredAt { get; } = Timestamp;
        }
    }
    
    • 说明:共享事件定义为WPF上位机提供统一事件模型,支持事件溯源和异步通信。

第1章 逃离单体地狱

1.1 迈向单体地狱的漫长旅程
  • 内容:单体膨胀导致复杂性。
  • 适用场景:WPF上位机单体耦合设备控制和监控逻辑。
  • C#代码扩展:单体设备控制逻辑(未拆分)。
    // SingleMonolith/DeviceController.cs
    namespace SingleMonolith
    {
        public class DeviceController
        {
            private readonly List<Device> _devices = new();
    
            public void SendCommand(int deviceId, string command)
            {
                var device = _devices.FirstOrDefault(d => d.Id == deviceId) ?? new Device(deviceId, "Device" + deviceId);
                device.Status = command == "Start" ? "Running" : "Stopped";
                _devices.Add(device);
                // 模拟数据库保存
                Console.WriteLine($"Device {deviceId} status: {device.Status}");
            }
    
            public string GetStatus(int deviceId)
            {
                return _devices.FirstOrDefault(d => d.Id == deviceId)?.Status ?? "Offline";
            }
    
            private class Device
            {
                public int Id { get; }
                public string Name { get; }
                public string Status { get; set; }
    
                public Device(int id, string name)
                {
                    Id = id;
                    Name = name;
                    Status = "Offline";
                }
            }
        }
    }
    
    • 说明:单体耦合命令和查询,扩展性差,后续章节拆分。
1.1.1 FTGO应用程序的架构
  • 内容:单体MVC架构。
  • 适用场景:WPF单体界面,耦合UI和逻辑。
  • C#代码扩展:WPF单体界面(MVVM)。
    // SingleMonolith/ViewModels/DeviceViewModel.cs
    namespace SingleMonolith.ViewModels
    {
        public class DeviceViewModel : INotifyPropertyChanged
        {
            private readonly DeviceController _controller = new();
            private string _status;
    
            public string Status
            {
                get => _status;
                set { _status = value; OnPropertyChanged(); }
            }
    
            public ICommand SendCommand { get; }
    
            public DeviceViewModel()
            {
                SendCommand = new RelayCommand(o => ExecuteCommand(o.ToString()));
                Status = _controller.GetStatus(1);
            }
    
            private void ExecuteCommand(string command)
            {
                _controller.SendCommand(1, command);
                Status = _controller.GetStatus(1);
            }
    
            public event PropertyChangedEventHandler PropertyChanged;
            private void OnPropertyChanged([CallerMemberName] string propertyName = null)
            {
                PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
            }
        }
    
        public class RelayCommand : ICommand
        {
            private readonly Action<object> _execute;
            public RelayCommand(Action<object> execute) => _execute = execute;
            public event EventHandler CanExecuteChanged;
            public bool CanExecute(object parameter) => true;
            public void Execute(object parameter) => _execute(parameter);
        }
    }
    
    • 说明:WPF单体界面,耦合业务逻辑,适合小规模。
1.1.2 单体架构的好处
  • 内容:简单开发、部署。
  • 适用场景:WPF单设备控制原型。
  • C#代码扩展:同上,无需扩展。
1.1.3 什么是单体地狱
  • 内容:代码膨胀、部署风险。
  • 适用场景:WPF多设备管理导致复杂性。
  • C#代码扩展:同上,模拟复杂单体逻辑。
1.2 为什么本书与你有关
  • 内容:提供微服务模式。
  • 适用场景:WPF开发者学习拆分。
  • C#代码扩展:无代码,理论指导。
1.3 你会在本书中学到什么
  • 内容:44个模式。
  • 适用场景:WPF架构规划。
  • C#代码扩展:无代码,理论指导。
1.4 拯救之道:微服务架构
  • 内容:微服务松耦合。
  • 适用场景:WPF拆分命令/查询服务。
  • C#代码扩展:定义微服务接口。
    // Shared/Interfaces/IDeviceService.cs
    namespace Shared.Interfaces
    {
        public interface IDeviceCommandService
        {
            Task SendCommandAsync(int deviceId, string command);
        }
    
        public interface IDeviceQueryService
        {
            Task<DeviceView> GetDeviceAsync(int deviceId);
        }
    
        public class DeviceView
        {
            public int DeviceId { get; set; }
            public string Status { get; set; }
            public DateTime LastUpdated { get; set; }
        }
    }
    
    • 说明:接口为WPF微服务化奠定基础。
1.4.1 扩展立方体和服务
  • 内容:X/Y/Z轴扩展。
  • 适用场景:WPF Y轴拆分命令/查询。
  • C#代码扩展:同上。
1.4.2 微服务架构作为模块化的一种形式
  • 内容:高内聚、低耦合。
  • 适用场景:WPF模块化设备控制。
  • C#代码扩展:同上。
1.4.3 每个服务拥有自己的数据库
  • 内容:数据库隔离。
  • 适用场景:WPF命令端MySQL,查询端Redis。
  • C#代码扩展:事件存储配置。
    // DeviceControlMicroservice/Infrastructure/Persistence/EventStoreContext.cs
    using Microsoft.EntityFrameworkCore;
    
    namespace DeviceControlMicroservice.Infrastructure.Persistence
    {
        public class EventStoreContext : DbContext
        {
            public DbSet<EventRecord> Events { get; set; }
    
            public EventStoreContext(DbContextOptions<EventStoreContext> options) : base(options) { }
        }
    
        public class EventRecord
        {
            public string Id { get; set; }
            public int AggregateId { get; set; }
            public string Type { get; set; }
            public string Payload { get; set; }
            public DateTime OccurredAt { get; set; }
        }
    }
    
    • 说明:命令端使用MySQL存储事件。
1.4.4 FTGO的微服务架构
  • 内容:FTGO拆分示例。
  • 适用场景:WPF拆分设备服务。
  • C#代码扩展:同上。
1.4.5 微服务架构与SOA的异同
  • 内容:微服务轻量。
  • 适用场景:WPF避免SOA复杂性。
  • C#代码扩展:无代码,理论指导。

第2章 服务的拆分策略

2.1 拆分单体
  • 内容:渐进拆分。
  • 适用场景:WPF单体迁移设备服务。
  • C#代码扩展:Strangler Fig模式。
    // DeviceControlMicroservice/Core/Application/UseCases/StranglerUseCase.cs
    namespace DeviceControlMicroservice.Core.Application.UseCases
    {
        public class StranglerUseCase
        {
            private readonly IDeviceCommandService _newService;
            private readonly LegacyDeviceController _legacy;
    
            public StranglerUseCase(IDeviceCommandService newService, LegacyDeviceController legacy)
            {
                _newService = newService;
                _legacy = legacy;
            }
    
            public async Task SendCommandAsync(int deviceId, string command)
            {
                if (deviceId <= 100) // 新设备用微服务
                    await _newService.SendCommandAsync(deviceId, command);
                else // 旧设备用单体
                    _legacy.SendCommand(deviceId, command);
            }
        }
    }
    
    • 说明:渐进替换单体逻辑。
2.2 领域驱动设计
  • 内容:DDD核心概念。
  • 适用场景:WPF设备限界上下文。
  • C#代码扩展:定义设备聚合。
    // DeviceControlMicroservice/Core/Domain/Aggregates/Device.cs
    namespace DeviceControlMicroservice.Core.Domain.Aggregates
    {
        public class Device
        {
            public int Id { get; private set; }
            public string Name { get; private set; }
            public string Status { get; private set; }
            private readonly List<IEvent> _domainEvents = new();
            public IReadOnlyList<IEvent> DomainEvents => _domainEvents.AsReadOnly();
    
            public Device(int id, string name)
            {
                Id = id;
                Name = name;
                Status = "Offline";
                _domainEvents.Add(new DeviceCreatedEvent(id, name, DateTime.UtcNow));
            }
    
            public void SendCommand(string command)
            {
                Status = command == "Start" ? "Running" : "Stopped";
                _domainEvents.Add(new DeviceCommandSentEvent(Id, command, Status, DateTime.UtcNow));
            }
    
            public void Apply(IEvent @event)
            {
                switch (@event)
                {
                    case DeviceCreatedEvent created:
                        Id = created.DeviceId;
                        Name = created.Name;
                        Status = "Offline";
                        break;
                    case DeviceCommandSentEvent cmd:
                        Status = cmd.Status;
                        break;
                }
            }
    
            public static Device Rebuild(IEnumerable<IEvent> events)
            {
                var device = new Device();
                foreach (var @event in events)
                    device.Apply(@event);
                return device;
            }
        }
    }
    
    • 说明:设备聚合支持事件溯源。
2.3 子领域
  • 内容:核心、支撑、通用子领域。
  • 适用场景:WPF核心设备控制子领域。
  • C#代码扩展:同上。
2.4 限界上下文
  • 内容:上下文边界。
  • 适用场景:WPF设备控制/监控上下文。
  • C#代码扩展:同上。
2.5 上下文映射
  • 内容:上下游交互。
  • 适用场景:WPF控制服务依赖监控。
  • C#代码扩展:上下文映射。
    // Shared/Interfaces/IContextMapper.cs
    namespace Shared.Interfaces
    {
        public interface IContextMapper
        {
            Task MapAsync(DeviceCommandSentEvent @event);
        }
    }
    
    • 说明:映射事件到监控服务。
2.6 单体中的限界上下文
  • 内容:单体内模块隔离。
  • 适用场景:WPF单体模块化。
  • C#代码扩展:同1.1.1。
2.7 拆分单体
  • 内容:按子领域拆分。
  • 适用场景:WPF拆分设备服务。
  • C#代码扩展:同2.1。
2.8 微服务拆分模式
  • 内容:Strangler Fig等。
  • 适用场景:WPF渐进迁移。
  • C#代码扩展:同2.1。

第3章 微服务架构中的进程间通信

3.1 微服务间的通信
  • 内容:同步/异步通信。
  • 适用场景:WPF调用设备服务。
  • C#代码扩展:REST和RocketMQ通信。
    // DeviceControlMicroservice/Core/Application/UseCases/DeviceCommunicationUseCase.cs
    using System.Net.Http;
    using Shared.Events;
    
    namespace DeviceControlMicroservice.Core.Application.UseCases
    {
        public class DeviceCommunicationUseCase
        {
            private readonly HttpClient _httpClient;
            private readonly IEventPublisher _publisher;
    
            public async Task SyncCallAsync(int deviceId, string command)
            {
                await _httpClient.PostAsync($"http://monitor-service/api/monitor/{deviceId}", null);
            }
    
            public async Task AsyncPublishAsync(DeviceCommandSentEvent @event)
            {
                await _publisher.PublishAsync(@event); // RocketMQ发布
            }
        }
    }
    
    • 说明:同步调用监控服务,异步发布事件。
3.2 同步通信模式
  • 内容:REST/gRPC。
  • 适用场景:WPF实时查询。
  • C#代码扩展:gRPC定义(proto略)。
3.3 异步通信模式
  • 内容:消息队列。
  • 适用场景:WPF事件通知。
  • C#代码扩展:RocketMQ发布。
    // DeviceControlMicroservice/Infrastructure/Messaging/RocketMqPublisher.cs
    namespace DeviceControlMicroservice.Infrastructure.Messaging
    {
        public class RocketMqPublisher : IEventPublisher
        {
            public async Task PublishAsync(IEvent @event)
            {
                // 模拟RocketMQ发布
                Console.WriteLine($"Published: {@event.EventId}");
                await Task.CompletedTask;
            }
        }
    }
    
3.4 消息代理
  • 内容:Kafka/RabbitMQ。
  • 适用场景:WPF高并发事件。
  • C#代码扩展:同上。
3.5 消息路由
  • 内容:主题路由。
  • 适用场景:WPF按设备ID路由。
  • C#代码扩展:同上。
3.6 事务支持的通信
  • 内容:事务性消息。
  • 适用场景:WPF指令事务。
  • C#代码扩展:Outbox模式。
    // DeviceControlMicroservice/Infrastructure/Persistence/Outbox.cs
    namespace DeviceControlMicroservice.Infrastructure.Persistence
    {
        public class Outbox
        {
            public string Id { get; set; }
            public string Payload { get; set; }
            public bool Processed { get; set; }
        }
    }
    

第4章 使用Saga管理事务

4.1 分布式事务简介
  • 内容:2PC缺点,Saga替代。
  • 适用场景:WPF指令+日志事务。
  • C#代码扩展:Saga状态机。
    // DeviceControlMicroservice/Core/Application/Sagas/DeviceSaga.cs
    using MassTransit;
    
    namespace DeviceControlMicroservice.Core.Application.Sagas
    {
        public class DeviceSaga : MassTransitStateMachine<DeviceSagaState>
        {
            public State CommandSent { get; private set; }
            public Event<DeviceCommandSentEvent> CommandEvent { get; private set; }
    
            public DeviceSaga()
            {
                InstanceState(x => x.CurrentState);
                Event(() => CommandEvent, x => x.CorrelateById(context => context.Message.DeviceId));
    
                Initially(
                    When(CommandEvent)
                        .Then(context => context.Saga.DeviceId = context.Message.DeviceId)
                        .TransitionTo(CommandSent));
            }
        }
    
        public class DeviceSagaState : SagaStateMachineInstance
        {
            public Guid CorrelationId { get; set; }
            public string CurrentState { get; set; }
            public int DeviceId { get; set; }
        }
    }
    
4.2 Saga模式
  • 内容:编排/编舞。
  • 适用场景:WPF指令Saga。
  • C#代码扩展:同上。
4.3 编排式Saga
  • 内容:事件驱动。
  • 适用场景:WPF松耦合事务。
  • C#代码扩展:同上。
4.4 编排式Saga
  • 内容:中央协调。
  • 适用场景:WPF复杂事务。
  • C#代码扩展:略。
4.5 Saga的实现
  • 内容:状态机。
  • 适用场景:WPF状态跟踪。
  • C#代码扩展:同4.1。

第5章 微服务架构中的业务逻辑设计

5.1 微服务业务逻辑
  • 内容:逻辑分布。
  • 适用场景:WPF设备逻辑拆分。
  • C#代码扩展:业务逻辑服务。
    // DeviceControlMicroservice/Core/Application/UseCases/DeviceLogicUseCase.cs
    namespace DeviceControlMicroservice.Core.Application.UseCases
    {
        public class DeviceLogicUseCase
        {
            private readonly IEventStore _eventStore;
    
            public async Task SendCommandAsync(int deviceId, string command)
            {
                var device = Device.Rebuild(await _eventStore.GetEventsAsync(deviceId));
                device.SendCommand(command);
                await _eventStore.SaveEventsAsync(deviceId, device.DomainEvents, device.Version);
            }
        }
    }
    
5.2 领域模型
  • 内容:DDD聚合。
  • 适用场景:WPF设备模型。
  • C#代码扩展:同2.2。
5.3 业务逻辑的分布方式
  • 内容:API/事件。
  • 适用场景:WPF服务协作。
  • C#代码扩展:同3.1。

第6章 使用事件溯源开发业务逻辑

6.1 事件溯源简介
  • 内容:事件状态来源。
  • 适用场景:WPF指令审计。
  • C#代码扩展:事件存储。
    // DeviceControlMicroservice/Infrastructure/Persistence/SqlEventStore.cs
    namespace DeviceControlMicroservice.Infrastructure.Persistence
    {
        public class SqlEventStore : IEventStore
        {
            private readonly EventStoreContext _context;
    
            public async Task SaveEventsAsync(int aggregateId, IEnumerable<IEvent> events, int expectedVersion)
            {
                foreach (var @event in events)
                {
                    _context.Events.Add(new EventRecord
                    {
                        Id = @event.EventId,
                        AggregateId = aggregateId,
                        Type = @event.GetType().Name,
                        Payload = System.Text.Json.JsonSerializer.Serialize(@event),
                        OccurredAt = @event.OccurredAt
                    });
                }
                await _context.SaveChangesAsync();
            }
    
            public async Task<IEnumerable<IEvent>> GetEventsAsync(int aggregateId)
            {
                var records = await _context.Events
                    .Where(e => e.AggregateId == aggregateId)
                    .OrderBy(e => e.OccurredAt)
                    .ToListAsync();
                return records.Select(r => System.Text.Json.JsonSerializer.Deserialize<IEvent>(r.Payload));
            }
        }
    }
    
6.2 事件溯源模式
  • 内容:事件存储/重放。
  • 适用场景:WPF状态重建。
  • C#代码扩展:同上。
6.3 事件存储
  • 内容:专用存储。
  • 适用场景:WPF事件持久化。
  • C#代码扩展:同上。
6.4 投影
  • 内容:事件到视图。
  • 适用场景:WPF查询优化。
  • C#代码扩展:投影到视图。
    // DeviceMonitorMicroservice/Core/Application/Queries/DeviceProjection.cs
    namespace DeviceMonitorMicroservice.Core.Application.Queries
    {
        public class DeviceProjection
        {
            private readonly IDeviceViewRepository _repository;
    
            public async Task ProjectAsync(DeviceCommandSentEvent @event)
            {
                var view = new DeviceView { DeviceId = @event.DeviceId, Status = @event.Status, LastUpdated = @event.Timestamp };
                await _repository.UpdateViewAsync(view);
            }
        }
    }
    

第7章 在微服务架构中实现查询

7.1 微服务查询
  • 内容:CQRS分离读写。
  • 适用场景:WPF实时查询。
  • C#代码扩展:查询用例。
    // DeviceMonitorMicroservice/Core/Application/Queries/DeviceQueryUseCase.cs
    namespace DeviceMonitorMicroservice.Core.Application.Queries
    {
        public class DeviceQueryUseCase
        {
            private readonly IDeviceViewRepository _repository;
    
            public async Task<DeviceView> GetDeviceAsync(int deviceId)
            {
                return await _repository.GetByIdAsync(deviceId);
            }
        }
    }
    
7.2 CQRS模式
  • 内容:命令/查询分离。
  • 适用场景:WPF命令/查询端。
  • C#代码扩展:同上。
7.3 事件溯源与CQRS
  • 内容:事件驱动视图。
  • 适用场景:WPF视图同步。
  • C#代码扩展:同6.4。
7.4 其他查询模式
  • 内容:API组合。
  • 适用场景:WPF复杂查询。
  • C#代码扩展:API组合。
    // DeviceMonitorMicroservice/Core/Application/Queries/CompositeQueryUseCase.cs
    namespace DeviceMonitorMicroservice.Core.Application.Queries
    {
        public class CompositeQueryUseCase
        {
            private readonly HttpClient _httpClient;
    
            public async Task<CompositeView> GetCompositeViewAsync(int deviceId)
            {
                var status = await _httpClient.GetStringAsync($"http://monitor-service/api/monitor/{deviceId}");
                var logs = await _httpClient.GetStringAsync($"http://log-service/api/logs/{deviceId}");
                return new CompositeView { Status = status, Logs = logs };
            }
        }
    
        public class CompositeView
        {
            public string Status { get; set; }
            public string Logs { get; set; }
        }
    }
    

第8章 外部API模式

8.1 外部API
  • 内容:API Gateway、BFF。
  • 适用场景:WPF通过Gateway调用。
  • C#代码扩展:Ocelot配置(见完整项目)。
8.2 API Gateway模式
  • 内容:统一入口。
  • 适用场景:WPF统一访问。
  • C#代码扩展:同上。
8.3 Backend for Frontend模式
  • 内容:客户端专用后端。
  • 适用场景:WPF专用API。
  • C#代码扩展:BFF服务。
    // DeviceMonitorMicroservice/Web/Controllers/BffController.cs
    [ApiController]
    [Route("api/bff")]
    public class BffController : ControllerBase
    {
        private readonly DeviceQueryUseCase _useCase;
    
        public async Task<IActionResult> GetDeviceForFrontend(int id)
        {
            var view = await _useCase.GetDeviceAsync(id);
            return Ok(new { view.DeviceId, view.Status }); // 简化WPF数据
        }
    }
    
8.4 其他外部API模式
  • 内容:GraphQL。
  • 适用场景:WPF复杂查询。
  • C#代码扩展:略。

第9章 微服务架构中的测试策略(上)

9.1 微服务测试
  • 内容:单元/集成测试。
  • 适用场景:WPF服务测试。
  • C#代码扩展:单元测试。
    // Tests/DeviceCommandUseCaseTests.cs
    using Moq;
    using Xunit;
    
    namespace Tests
    {
        public class DeviceCommandUseCaseTests
        {
            private readonly Mock<IEventStore> _eventStoreMock = new();
            private readonly DeviceLogicUseCase _useCase;
    
            public DeviceCommandUseCaseTests()
            {
                _useCase = new DeviceLogicUseCase(_eventStoreMock.Object);
            }
    
            [Fact]
            public async Task SendCommand_SavesEvent()
            {
                _eventStoreMock.Setup(s => s.GetEventsAsync(1)).ReturnsAsync(new List<IEvent>());
                await _useCase.SendCommandAsync(1, "Start");
                _eventStoreMock.Verify(s => s.SaveEventsAsync(1, It.IsAny<IEnumerable<IEvent>>(), 0), Times.Once());
            }
        }
    }
    
9.2 测试驱动开发
  • 内容:TDD。
  • 适用场景:WPF用例开发。
  • C#代码扩展:同上。
9.3 单元测试
  • 内容:聚合测试。
  • 适用场景:WPF领域测试。
  • C#代码扩展:同上。
9.4 集成测试
  • 内容:服务集成。
  • 适用场景:WPF服务测试。
  • C#代码扩展:略。

第10章 微服务架构中的测试策略(下)

10.1 服务测试
  • 内容:端到端服务。
  • 适用场景:WPF服务测试。
  • C#代码扩展:服务测试。
    // Tests/DeviceServiceTests.cs
    [Fact]
    public async Task DeviceService_SendsCommand()
    {
        var client = new HttpClient();
        var response = await client.PostAsync("http://device-control-service/api/device/1/command", new StringContent("\"Start\""));
        Assert.True(response.IsSuccessStatusCode);
    }
    
10.2 契约测试
  • 内容:Pact测试。
  • 适用场景:WPF API契约。
  • C#代码扩展:Pact测试。
    // Tests/DevicePactTest.cs
    using PactNet;
    
    namespace Tests
    {
        public class DevicePactTest : IDisposable
        {
            private readonly IPactBuilder _pact;
    
            public DevicePactTest()
            {
                _pact = Pact.V3("WPFClient", "DeviceService", new PactConfig());
            }
    
            [Fact]
            public async Task TestDeviceContract()
            {
                _pact.UponReceiving("Command request")
                    .Given("Device exists")
                    .WithRequest(HttpMethod.Post, "/api/device/1/command")
                    .WithJsonBody(new { Command = "Start" })
                    .WillRespond()
                    .WithStatus(HttpStatusCode.OK);
    
                await _pact.VerifyAsync(async ctx =>
                {
                    var client = new HttpClient { BaseAddress = new Uri(ctx.MockServerUri) };
                    await client.PostAsync("/api/device/1/command", new StringContent(JsonSerializer.Serialize(new { Command = "Start" })));
                });
            }
    
            public void Dispose() => _pact.Dispose();
        }
    }
    
10.3 端到端测试
  • 内容:系统测试。
  • 适用场景:WPF整体测试。
  • C#代码扩展:略。

第11章 开发面向生产环境的微服务应用

11.1 服务组件
  • 内容:配置、日志。
  • 适用场景:WPF生产部署。
  • C#代码扩展:Serilog日志。
    // DeviceControlMicroservice/Program.cs
    Log.Logger = new LoggerConfiguration()
        .WriteTo.Console()
        .WriteTo.File("logs/device.txt")
        .CreateLogger();
    
    services.AddLogging(builder => builder.AddSerilog());
    
11.2 配置管理
  • 内容:外部配置。
  • 适用场景:WPF动态配置。
  • C#代码扩展:appsettings.json。
    // DeviceControlMicroservice/Program.cs
    var configuration = new ConfigurationBuilder()
        .AddJsonFile("appsettings.json")
        .Build();
    services.AddSingleton<IConfiguration>(configuration);
    
11.3 结构化日志
  • 内容:日志聚合。
  • 适用场景:WPF监控。
  • C#代码扩展:同11.1。
11.4 健康检查和准备检查
  • 内容:服务健康。
  • 适用场景:WPF服务可用性。
  • C#代码扩展:健康检查。
    // DeviceControlMicroservice/Startup.cs
    services.AddHealthChecks()
        .AddCheck("self", () => HealthCheckResult.Healthy());
    app.UseHealthChecks("/health");
    

第12章 部署微服务应用

12.1 部署微服务
  • 内容:容器化。
  • 适用场景:WPF微服务部署。
  • C#代码扩展:Dockerfile。
    FROM mcr.microsoft.com/dotnet/aspnet:6.0
    COPY bin/Release/net6.0/publish/ /app
    WORKDIR /app
    ENTRYPOINT ["dotnet", "DeviceControlMicroservice.dll"]
    
12.2 使用Docker部署
  • 内容:Docker镜像。
  • 适用场景:WPF容器化。
  • C#代码扩展:同上。
12.3 使用Kubernetes部署
  • 内容:K8s编排。
  • 适用场景:WPF高可用。
  • C#代码扩展:K8s YAML(略)。
12.4 其他部署模式
  • 内容:蓝绿部署。
  • 适用场景:WPF零停机。
  • C#代码扩展:略。

第13章 微服务架构的重构策略

13.1 重构单体
  • 内容:Strangler Fig。
  • 适用场景:WPF单体迁移。
  • C#代码扩展:同2.1。
13.2 重构策略
  • 内容:剥离/分支。
  • 适用场景:WPF模块剥离。
  • C#代码扩展:同2.1。
13.3 遗漏的模式
  • 内容:服务网格。
  • 适用场景:WPF高级集成。
  • C#代码扩展:Consul服务发现。
    // DeviceControlMicroservice/Infrastructure/Discovery/ConsulClient.cs
    using Consul;
    
    namespace DeviceControlMicroservice.Infrastructure.Discovery
    {
        public class ConsulClient
        {
            private readonly IConsulClient _client;
    
            public async Task<string> DiscoverServiceAsync(string serviceName)
            {
                var services = await _client.Catalog.Service(serviceName);
                return services.Response.First().ServiceAddress;
            }
        }
    }
    

二、WPF完整项目

基于《微服务架构设计模式》,设计WPF上位机项目,管理200+设备,支持高并发指令和实时监控。架构结合整洁架构、DDD、事件溯源、CQRS,集成阿里云(RDS、RocketMQ、Redis)、Ocelot Gateway和SignalR。

项目结构

Solution/
├── Shared/                          // 共享事件和接口
│   ├── Events/
│   │   └── DeviceEvent.cs
│   ├── Interfaces/
│   │   └── IDeviceService.cs
├── DeviceControlMicroservice/       // 命令端微服务
│   ├── Core/
│   │   ├── Domain/Aggregates/Device.cs
│   │   ├── Application/UseCases/DeviceLogicUseCase.cs
│   │   ├── Application/Sagas/DeviceSaga.cs
│   ├── Infrastructure/
│   │   ├── Persistence/SqlEventStore.cs
│   │   ├── Messaging/RocketMqPublisher.cs
│   ├── Web/Controllers/DeviceController.cs
├── DeviceMonitorMicroservice/      // 查询端微服务
│   ├── Core/
│   │   ├── Application/Queries/DeviceQueryUseCase.cs
│   ├── Infrastructure/
│   │   ├── Persistence/SqlDeviceViewRepository.cs
│   ├── Web/Controllers/MonitorController.cs
├── Gateway/                        // API Gateway (Ocelot)
│   └── ocelot.json
├── UI.WPF/                         // WPF上位机
│   ├── ViewModels/DeviceViewModel.cs
│   ├── Views/MainWindow.xaml
│   ├── Services/SignalRClient.cs
├── Tests/
│   └── DeviceTests.cs

详细代码

1. 共享事件和接口 (Shared)
// Shared/Events/DeviceEvent.cs
namespace Shared.Events
{
    public interface IEvent
    {
        string EventId { get; }
        DateTime OccurredAt { get; }
    }

    public record DeviceCreatedEvent(int DeviceId, string Name, DateTime Timestamp) : IEvent
    {
        public string EventId { get; } = Guid.NewGuid().ToString();
        public DateTime OccurredAt { get; } = Timestamp;
    }

    public record DeviceCommandSentEvent(int DeviceId, string Command, string Status, DateTime Timestamp) : IEvent
    {
        public string EventId { get; } = Guid.NewGuid().ToString();
        public DateTime OccurredAt { get; } = Timestamp;
    }
}

// Shared/Interfaces/IDeviceService.cs
namespace Shared.Interfaces
{
    public interface IDeviceCommandService
    {
        Task SendCommandAsync(int deviceId, string command);
    }

    public interface IDeviceQueryService
    {
        Task<DeviceView> GetDeviceAsync(int deviceId);
        Task<IEnumerable<DeviceView>> GetAllDevicesAsync();
    }

    public class DeviceView
    {
        public int DeviceId { get; set; }
        public string Status { get; set; }
        public DateTime LastUpdated { get; set; }
    }
}
2. 命令端微服务 (DeviceControlMicroservice)
  • 聚合 (Core/Domain/Aggregates/Device.cs)

    namespace DeviceControlMicroservice.Core.Domain.Aggregates
    {
        public class Device
        {
            public int Id { get; private set; }
            public string Name { get; private set; }
            public string Status { get; private set; }
            public int Version { get; private set; }
            private readonly List<IEvent> _domainEvents = new();
            public IReadOnlyList<IEvent> DomainEvents => _domainEvents.AsReadOnly();
    
            private Device() { }
    
            public Device(int id, string name)
            {
                Id = id;
                Name = name;
                Status = "Offline";
                Version = 0;
                _domainEvents.Add(new DeviceCreatedEvent(id, name, DateTime.UtcNow));
            }
    
            public void SendCommand(string command)
            {
                Status = command == "Start" ? "Running" : "Stopped";
                Version++;
                _domainEvents.Add(new DeviceCommandSentEvent(Id, command, Status, DateTime.UtcNow));
            }
    
            public void Apply(IEvent @event)
            {
                switch (@event)
                {
                    case DeviceCreatedEvent created:
                        Id = created.DeviceId;
                        Name = created.Name;
                        Status = "Offline";
                        Version = 0;
                        break;
                    case DeviceCommandSentEvent cmd:
                        Status = cmd.Status;
                        Version++;
                        break;
                }
            }
    
            public static Device Rebuild(IEnumerable<IEvent> events)
            {
                var device = new Device();
                foreach (var @event in events)
                    device.Apply(@event);
                return device;
            }
    
            public void ClearDomainEvents() => _domainEvents.Clear();
        }
    }
    
  • 用例 (Core/Application/UseCases/DeviceLogicUseCase.cs)

    namespace DeviceControlMicroservice.Core.Application.UseCases
    {
        public class DeviceLogicUseCase : IDeviceCommandService
        {
            private readonly IEventStore _eventStore;
            private readonly IEventPublisher _publisher;
    
            public DeviceLogicUseCase(IEventStore eventStore, IEventPublisher publisher)
            {
                _eventStore = eventStore;
                _publisher = publisher;
            }
    
            public async Task SendCommandAsync(int deviceId, string command)
            {
                var events = await _eventStore.GetEventsAsync(deviceId);
                var device = Device.Rebuild(events);
                device.SendCommand(command);
                await _eventStore.SaveEventsAsync(deviceId, device.DomainEvents, device.Version);
                foreach (var @event in device.DomainEvents)
                    await _publisher.PublishAsync(@event);
                device.ClearDomainEvents();
            }
        }
    }
    
  • 事件存储 (Infrastructure/Persistence/SqlEventStore.cs)

    using Microsoft.EntityFrameworkCore;
    using Shared.Events;
    
    namespace DeviceControlMicroservice.Infrastructure.Persistence
    {
        public interface IEventStore
        {
            Task SaveEventsAsync(int aggregateId, IEnumerable<IEvent> events, int expectedVersion);
            Task<IEnumerable<IEvent>> GetEventsAsync(int aggregateId);
        }
    
        public class EventStoreContext : DbContext
        {
            public DbSet<EventRecord> Events { get; set; }
            public DbSet<OutboxMessage> OutboxMessages { get; set; }
    
            public EventStoreContext(DbContextOptions<EventStoreContext> options) : base(options) { }
        }
    
        public class EventRecord
        {
            public string Id { get; set; }
            public int AggregateId { get; set; }
            public int Version { get; set; }
            public string Type { get; set; }
            public string Payload { get; set; }
            public DateTime OccurredAt { get; set; }
        }
    
        public class OutboxMessage
        {
            public string Id { get; set; }
            public string Type { get; set; }
            public string Payload { get; set; }
            public DateTime CreatedAt { get; set; }
            public bool Processed { get; set; }
        }
    
        public class SqlEventStore : IEventStore
        {
            private readonly EventStoreContext _context;
    
            public SqlEventStore(EventStoreContext context)
            {
                _context = context;
            }
    
            public async Task SaveEventsAsync(int aggregateId, IEnumerable<IEvent> events, int expectedVersion)
            {
                var currentVersion = await _context.Events
                    .Where(e => e.AggregateId == aggregateId)
                    .MaxAsync(e => (int?)e.Version) ?? -1;
    
                if (currentVersion != expectedVersion)
                    throw new InvalidOperationException("Concurrency conflict.");
    
                foreach (var @event in events)
                {
                    _context.Events.Add(new EventRecord
                    {
                        Id = @event.EventId,
                        AggregateId = aggregateId,
                        Version = expectedVersion + 1,
                        Type = @event.GetType().Name,
                        Payload = System.Text.Json.JsonSerializer.Serialize(@event),
                        OccurredAt = @event.OccurredAt
                    });
                    _context.OutboxMessages.Add(new OutboxMessage
                    {
                        Id = @event.EventId,
                        Type = @event.GetType().Name,
                        Payload = System.Text.Json.JsonSerializer.Serialize(@event),
                        CreatedAt = DateTime.UtcNow,
                        Processed = false
                    });
                }
                await _context.SaveChangesAsync();
            }
    
            public async Task<IEnumerable<IEvent>> GetEventsAsync(int aggregateId)
            {
                var records = await _context.Events
                    .Where(e => e.AggregateId == aggregateId)
                    .OrderBy(e => e.Version)
                    .ToListAsync();
                return records.Select(r => (IEvent)System.Text.Json.JsonSerializer.Deserialize(r.Payload, Type.GetType($"Shared.Events.{r.Type}")));
            }
        }
    }
    
  • 消息发布 (Infrastructure/Messaging/RocketMqPublisher.cs)

    namespace DeviceControlMicroservice.Infrastructure.Messaging
    {
        public interface IEventPublisher
        {
            Task PublishAsync(IEvent @event);
        }
    
        public class RocketMqPublisher : IEventPublisher
        {
            private readonly EventStoreContext _context;
    
            public RocketMqPublisher(EventStoreContext context)
            {
                _context = context;
            }
    
            public async Task PublishAsync(IEvent @event)
            {
                Console.WriteLine($"Publishing: {@event.EventId}"); // 模拟RocketMQ
                var message = await _context.OutboxMessages.FirstOrDefaultAsync(m => m.Id == @event.EventId);
                if (message != null)
                {
                    message.Processed = true;
                    await _context.SaveChangesAsync();
                }
            }
        }
    }
    
  • API (Web/Controllers/DeviceController.cs)

    using Microsoft.AspNetCore.Mvc;
    using Shared.Interfaces;
    
    namespace DeviceControlMicroservice.Web.Controllers
    {
        [ApiController]
        [Route("api/device")]
        public class DeviceController : ControllerBase
        {
            private readonly IDeviceCommandService _service;
    
            public DeviceController(IDeviceCommandService service)
            {
                _service = service;
            }
    
            [HttpPost("{id}/command")]
            public async Task<IActionResult> SendCommand(int id, [FromBody] string command)
            {
                await _service.SendCommandAsync(id, command);
                return Ok();
            }
        }
    }
    
3. 查询端微服务 (DeviceMonitorMicroservice)
  • 查询用例 (Core/Application/Queries/DeviceQueryUseCase.cs)

    namespace DeviceMonitorMicroservice.Core.Application.Queries
    {
        public class DeviceQueryUseCase : IDeviceQueryService
        {
            private readonly IDeviceViewRepository _repository;
    
            public DeviceQueryUseCase(IDeviceViewRepository repository)
            {
                _repository = repository;
            }
    
            public async Task<DeviceView> GetDeviceAsync(int deviceId)
            {
                return await _repository.GetByIdAsync(deviceId);
            }
    
            public async Task<IEnumerable<DeviceView>> GetAllDevicesAsync()
            {
                return await _repository.GetAllAsync();
            }
        }
    }
    
  • 视图存储 (Infrastructure/Persistence/SqlDeviceViewRepository.cs)

    namespace DeviceMonitorMicroservice.Infrastructure.Persistence
    {
        public interface IDeviceViewRepository
        {
            Task<DeviceView> GetByIdAsync(int deviceId);
            Task<IEnumerable<DeviceView>> GetAllAsync();
            Task UpdateViewAsync(DeviceView view);
        }
    
        public class SqlDeviceViewRepository : IDeviceViewRepository
        {
            private readonly EventStoreContext _context;
    
            public SqlDeviceViewRepository(EventStoreContext context)
            {
                _context = context;
            }
    
            public async Task<DeviceView> GetByIdAsync(int deviceId)
            {
                return await _context.DeviceViews.FindAsync(deviceId);
            }
    
            public async Task<IEnumerable<DeviceView>> GetAllAsync()
            {
                return await _context.DeviceViews.ToListAsync();
            }
    
            public async Task UpdateViewAsync(DeviceView view)
            {
                var existing = await _context.DeviceViews.FindAsync(view.DeviceId);
                if (existing == null)
                    _context.DeviceViews.Add(view);
                else
                    _context.Entry(existing).CurrentValues.SetValues(view);
                await _context.SaveChangesAsync();
            }
        }
    }
    
  • 事件消费者 (Infrastructure/Messaging/EventConsumer.cs)

    namespace DeviceMonitorMicroservice.Infrastructure.Messaging
    {
        public class EventConsumer
        {
            private readonly IDeviceViewRepository _repository;
    
            public EventConsumer(IDeviceViewRepository repository)
            {
                _repository = repository;
            }
    
            public async Task Consume(DeviceCommandSentEvent @event)
            {
                await _repository.UpdateViewAsync(new DeviceView
                {
                    DeviceId = @event.DeviceId,
                    Status = @event.Status,
                    LastUpdated = @event.Timestamp
                });
            }
        }
    }
    
  • API (Web/Controllers/MonitorController.cs)

    using Microsoft.AspNetCore.Mvc;
    using Shared.Interfaces;
    
    namespace DeviceMonitorMicroservice.Web.Controllers
    {
        [ApiController]
        [Route("api/monitor")]
        public class MonitorController : ControllerBase
        {
            private readonly IDeviceQueryService _service;
    
            public MonitorController(IDeviceQueryService service)
            {
                _service = service;
            }
    
            [HttpGet("{id}")]
            public async Task<IActionResult> GetDevice(int id)
            {
                return Ok(await _service.GetDeviceAsync(id));
            }
    
            [HttpGet]
            public async Task<IActionResult> GetAllDevices()
            {
                return Ok(await _service.GetAllDevicesAsync());
            }
        }
    }
    
4. API Gateway (Gateway)
  • Ocelot配置 (ocelot.json)

    {
      "Routes": [
        {
          "DownstreamPathTemplate": "/api/device/{id}/command",
          "DownstreamScheme": "http",
          "DownstreamHostAndPorts": [{ "Host": "device-control-service", "Port": 80 }],
          "UpstreamPathTemplate": "/device/{id}/command",
          "UpstreamHttpMethod": [ "POST" ]
        },
        {
          "DownstreamPathTemplate": "/api/monitor/{id}",
          "DownstreamScheme": "http",
          "DownstreamHostAndPorts": [{ "Host": "device-monitor-service", "Port": 80 }],
          "UpstreamPathTemplate": "/monitor/{id}",
          "UpstreamHttpMethod": [ "GET" ]
        }
      ],
      "GlobalConfiguration": {
        "BaseUrl": "http://localhost:5000"
      }
    }
    
  • Startup (Program.cs)

    using Ocelot.DependencyInjection;
    using Ocelot.Middleware;
    
    var builder = WebApplication.CreateBuilder(args);
    builder.Configuration.AddJsonFile("ocelot.json");
    builder.Services.AddOcelot();
    var app = builder.Build();
    app.UseOcelot().Wait();
    app.Run();
    
5. WPF上位机 (UI.WPF)
  • ViewModel (ViewModels/DeviceViewModel.cs)

    using Microsoft.AspNetCore.SignalR.Client;
    using System.Collections.ObjectModel;
    using System.ComponentModel;
    using System.Net.Http;
    using System.Text.Json;
    using System.Windows.Input;
    using Shared.Interfaces;
    
    namespace UI.WPF.ViewModels
    {
        public class DeviceViewModel : INotifyPropertyChanged
        {
            private readonly HttpClient _httpClient;
            private readonly HubConnection _hubConnection;
            private ObservableCollection<DeviceView> _devices = new();
            private string _command;
    
            public DeviceViewModel(HttpClient httpClient)
            {
                _httpClient = httpClient;
                SendCommand = new RelayCommand(async () => await ExecuteSendCommand());
                LoadDevices = new RelayCommand(async () => await ExecuteLoadDevices());
                _hubConnection = new HubConnectionBuilder()
                    .WithUrl("http://localhost:5000/eventHub")
                    .Build();
                _hubConnection.On<DeviceCommandSentEvent>("ReceiveEvent", async @event =>
                {
                    var device = await GetDeviceAsync(@event.DeviceId);
                    var existing = _devices.FirstOrDefault(d => d.DeviceId == device.DeviceId);
                    if (existing != null)
                        _devices[_devices.IndexOf(existing)] = device;
                    else
                        _devices.Add(device);
                });
                _hubConnection.StartAsync();
            }
    
            public ObservableCollection<DeviceView> Devices
            {
                get => _devices;
                set { _devices = value; OnPropertyChanged(); }
            }
    
            public string Command
            {
                get => _command;
                set { _command = value; OnPropertyChanged(); }
            }
    
            public ICommand SendCommand { get; }
            public ICommand LoadDevices { get; }
    
            private async Task ExecuteSendCommand()
            {
                var content = new StringContent(JsonSerializer.Serialize(Command), System.Text.Encoding.UTF8, "application/json");
                await _httpClient.PostAsync("http://localhost:5000/device/1/command", content);
            }
    
            private async Task ExecuteLoadDevices()
            {
                var response = await _httpClient.GetAsync("http://localhost:5000/monitor");
                var devices = JsonSerializer.Deserialize<IEnumerable<DeviceView>>(await response.Content.ReadAsStringAsync());
                Devices = new ObservableCollection<DeviceView>(devices);
            }
    
            private async Task<DeviceView> GetDeviceAsync(int deviceId)
            {
                var response = await _httpClient.GetAsync($"http://localhost:5000/monitor/{deviceId}");
                return JsonSerializer.Deserialize<DeviceView>(await response.Content.ReadAsStringAsync());
            }
    
            public event PropertyChangedEventHandler PropertyChanged;
            private void OnPropertyChanged([CallerMemberName] string name = null)
            {
                PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(name));
            }
        }
    
        public class RelayCommand : ICommand
        {
            private readonly Func<Task> _execute;
            public RelayCommand(Func<Task> execute) => _execute = execute;
            public event EventHandler CanExecuteChanged;
            public bool CanExecute(object parameter) => true;
            public async void Execute(object parameter) => await _execute();
        }
    }
    
  • XAML (Views/MainWindow.xaml)

    <Window x:Class="UI.WPF.Views.MainWindow"
            xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
            Title="Device Control" Height="400" Width="600">
        <Grid>
            <StackPanel Margin="10">
                <DataGrid ItemsSource="{Binding Devices}" AutoGenerateColumns="True" Height="200" />
                <TextBox Text="{Binding Command}" Width="200" Margin="0,10" />
                <Button Content="Send Command" Command="{Binding SendCommand}" />
                <Button Content="Load Devices" Command="{Binding LoadDevices}" Margin="0,10" />
            </StackPanel>
        </Grid>
    </Window>
    
  • SignalR (Services/SignalRClient.cs)

    using Microsoft.AspNetCore.SignalR;
    using Shared.Events;
    
    namespace UI.WPF.Services
    {
        public class EventHub : Hub
        {
            public async Task SendEvent(DeviceCommandSentEvent @event)
            {
                await Clients.All.SendAsync("ReceiveEvent", @event);
            }
        }
    }
    
  • App.xaml.cs

    using Microsoft.Extensions.DependencyInjection;
    using System.Net.Http;
    using UI.WPF.ViewModels;
    
    namespace UI.WPF
    {
        public partial class App : Application
        {
            private readonly ServiceProvider _serviceProvider;
    
            public App()
            {
                var services = new ServiceCollection();
                services.AddHttpClient();
                services.AddSingleton<DeviceViewModel>();
                _serviceProvider = services.BuildServiceProvider();
            }
    
            protected override void OnStartup(StartupEventArgs e)
            {
                base.OnStartup(e);
                var vm = _serviceProvider.GetService<DeviceViewModel>();
                var window = new MainWindow { DataContext = vm };
                window.Show();
            }
        }
    }
    
6. 测试 (Tests)
// Tests/DeviceTests.cs
using Moq;
using Xunit;
using DeviceControlMicroservice.Core.Application.UseCases;
using Shared.Events;

namespace Tests
{
    public class DeviceTests
    {
        private readonly Mock<IEventStore> _eventStoreMock = new();
        private readonly Mock<IEventPublisher> _publisherMock = new();
        private readonly DeviceLogicUseCase _useCase;

        public DeviceTests()
        {
            _useCase = new DeviceLogicUseCase(_eventStoreMock.Object, _publisherMock.Object);
        }

        [Fact]
        public async Task SendCommand_PublishesEvent()
        {
            _eventStoreMock.Setup(s => s.GetEventsAsync(1)).ReturnsAsync(new List<IEvent> { new DeviceCreatedEvent(1, "Device1", DateTime.UtcNow) });
            await _useCase.SendCommandAsync(1, "Start");
            _publisherMock.Verify(p => p.PublishAsync(It.IsAny<DeviceCommandSentEvent>()), Times.Once());
        }
    }
}

优化与部署

  • 高并发:
    • Redis缓存DeviceView。
    • RocketMQ分区按设备ID。
    • Sentinel限流指令请求。
  • 部署:
    • Docker容器化各微服务。
    • Kubernetes部署高可用。
  • 国内生态:
    • 阿里云RDS存储事件。
    • RocketMQ异步通信。
    • SLS日志,SkyWalking追踪。

三、进一步指导

请告知是否需要:

  • 特定模式深入:如Saga、CQRS优化。
  • WPF扩展:多设备列表、历史查询。
  • 国内案例:阿里云/TencentDB集成。
  • 测试扩展:更多契约测试。
  • 半导体场景:多设备调度。

提供细节,我将进一步定制!

Logo

码道开发者社区,聚焦华为云码道 CodeArts 代码智能体,沉淀 Agent、Skill、鸿蒙开发实战内容,供开发者查阅资料、交流技术、分享工程实践

更多推荐