《微服务架构设计模式》每章每节C#代码扩展
《微服务架构设计模式》每章每节提供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限流指令请求。
- Redis缓存
- 部署:
- Docker容器化各微服务。
- Kubernetes部署高可用。
- 国内生态:
- 阿里云RDS存储事件。
- RocketMQ异步通信。
- SLS日志,SkyWalking追踪。
三、进一步指导
请告知是否需要:
- 特定模式深入:如Saga、CQRS优化。
- WPF扩展:多设备列表、历史查询。
- 国内案例:阿里云/TencentDB集成。
- 测试扩展:更多契约测试。
- 半导体场景:多设备调度。
提供细节,我将进一步定制!
更多推荐



所有评论(0)