# WCS DDD重构实施计划 > **For agentic workers:** REQUIRED: Use superpowers:subagent-driven-development (if subagents available) or superpowers:executing-plans to implement this plan. Steps use checkbox (`- [ ]`) syntax for tracking. **目标:** 实现WCS系统的渐进式DDD重构,包括领域层、应用层和基础设施层,重点重构设备管理领域 **架构:** 采用标准的四层DDD架构:表现层 → 应用层 → 领域层 → 基础设施层,使用CQRS模式分离命令和查询,领域事件驱动设计 **技术栈:** - .NET 6.0 - SqlSugar ORM - Redis(StackExchange.Redis) - xUnit + Moq(测试框架) - Autofac(依赖注入) --- ## Chunk 1: 项目结构搭建和基础设施类 ### Task 1: 创建领域层项目结构 **Files:** - Create: `WIDESEAWCS_Domain/WIDESEAWCS_Domain.csproj` - Create: `WIDESEAWCS_Domain/Common/AggregateRoot.cs` - Create: `WIDESEAWCS_Domain/Common/IDomainEvent.cs` - Create: `WIDESEAWCS_Domain/Common/DomainEvent.cs` - Create: `WIDESEAWCS_Domain/Common/DomainException.cs` - Test: `WIDESEAWCS_Domain.Tests/AggregateRootTests.cs` - [ ] **Step 1: 创建领域层项目文件** ```xml net6.0 enable enable ``` - [ ] **Step 2: 创建聚合根基类** ```csharp namespace WIDESEAWCS_Domain.Common; public abstract class AggregateRoot { public TId Id { get; protected set; } = default!; public int Version { get; private set; } private readonly List _domainEvents = new(); protected AggregateRoot() { } protected AggregateRoot(TId id) { Id = id; } public IReadOnlyCollection GetDomainEvents() => _domainEvents.AsReadOnly(); public void ClearDomainEvents() => _domainEvents.Clear(); protected void AddDomainEvent(IDomainEvent domainEvent) { _domainEvents.Add(domainEvent); } protected void IncrementVersion() { Version++; } } ``` - [ ] **Step 3: 创建领域事件接口** ```csharp namespace WIDESEAWCS_Domain.Common; public interface IDomainEvent { DateTime OccurredOn { get; } } ``` - [ ] **Step 4: 创建领域事件基类** ```csharp namespace WIDESEAWCS_Domain.Common; public abstract record DomainEvent : IDomainEvent { public DateTime OccurredOn { get; init; } = DateTime.UtcNow; } ``` - [ ] **Step 5: 创建领域异常类** ```csharp namespace WIDESEAWCS_Domain.Common; public abstract class DomainException : Exception { public string ErrorCode { get; } protected DomainException(string message, string errorCode = "DOMAIN_ERROR") : base(message) { ErrorCode = errorCode; } } public class InvalidDomainOperationException : DomainException { public InvalidDomainOperationException(string message) : base(message, "INVALID_OPERATION") { } } ``` - [ ] **Step 6: 创建聚合根基类测试** ```csharp using Xunit; using WIDESEAWCS_Domain.Common; namespace WIDESEAWCS_Domain.Tests; public class AggregateRootTests { [Fact] public void Constructor_Should_SetId() { // Arrange & Act var aggregate = new TestAggregate(1); // Assert Assert.Equal(1, aggregate.Id); } [Fact] public void AddDomainEvent_Should_AddEvent() { // Arrange var aggregate = new TestAggregate(1); var domainEvent = new TestDomainEvent(); // Act aggregate.TestAddDomainEvent(domainEvent); // Assert var events = aggregate.GetDomainEvents(); Assert.Single(events); Assert.Same(domainEvent, events.First()); } [Fact] public void ClearDomainEvents_Should_RemoveAllEvents() { // Arrange var aggregate = new TestAggregate(1); aggregate.TestAddDomainEvent(new TestDomainEvent()); // Act aggregate.ClearDomainEvents(); // Assert Assert.Empty(aggregate.GetDomainEvents()); } [Fact] public void IncrementVersion_Should_IncreaseVersion() { // Arrange var aggregate = new TestAggregate(1); var initialVersion = aggregate.Version; // Act aggregate.TestIncrementVersion(); // Assert Assert.Equal(initialVersion + 1, aggregate.Version); } // 测试辅助类 private class TestAggregate : AggregateRoot { public TestAggregate(int id) : base(id) { } public void TestAddDomainEvent(IDomainEvent domainEvent) => AddDomainEvent(domainEvent); public void TestIncrementVersion() => IncrementVersion(); } private record TestDomainEvent : DomainEvent { } } ``` - [ ] **Step 7: 运行测试验证基础类** Run: `dotnet test WIDESEAWCS_Domain.Tests --filter "FullyQualifiedName~AggregateRootTests"` Expected: All tests pass - [ ] **Step 8: 提交基础类** ```bash git add WIDESEAWCS_Domain/ git commit -m "feat: 添加DDD基础类 - AggregateRoot, IDomainEvent, DomainException" ``` --- ### Task 2: 创建应用层项目结构 **Files:** - Create: `WIDESEAWCS_Application/WIDESEAWCS_Application.csproj` - Create: `WIDESEAWCS_Application/Common/IRepository.cs` - Create: `WIDESEAWCS_Application/Common/IUnitOfWork.cs` - [ ] **Step 1: 创建应用层项目文件** ```xml net6.0 enable enable ``` - [ ] **Step 2: 创建仓储基接口** ```csharp using WIDESEAWCS_Domain.Common; namespace WIDESEAWCS_Application.Common; public interface IRepository where TEntity : AggregateRoot { Task GetById(TId id); Task Add(TEntity entity); Task Update(TEntity entity); Task Delete(TId id); } ``` - [ ] **Step 3: 创建工作单元接口** ```csharp namespace WIDESEAWCS_Application.Common; public interface IUnitOfWork : IDisposable { ITransaction BeginTransaction(); Task Commit(); Task Rollback(); } public interface ITransaction : IDisposable { Task Commit(); Task Rollback(); } ``` - [ ] **Step 4: 提交应用层基础接口** ```bash git add WIDESEAWCS_Application/ git commit -m "feat: 添加应用层基础接口 - IRepository, IUnitOfWork" ``` --- ### Task 3: 创建基础设施层项目结构 **Files:** - Create: `WIDESEAWCS_Infrastructure/WIDESEAWCS_Infrastructure.csproj` - Create: `WIDESEAWCS_Infrastructure/Persistence/InMemoryUnitOfWork.cs` - Create: `WIDESEAWCS_Infrastructure/Persistence/InMemoryTransaction.cs` - [ ] **Step 1: 创建**基础设施层项目文件** ```xml net6.0 enable enable ``` - [ ] **Step 2: 创建内存工作单元实现** ```csharp using WIDESEAWCS_Application.Common; namespace WIDESEAWCS_Infrastructure.Persistence; public class InMemoryUnitOfWork : IUnitOfWork { private readonly List _operations = new(); public ITransaction BeginTransaction() { return new InMemoryTransaction(this); } public Task Commit() { foreach (var operation in _operations) { operation(); } _operations.Clear(); return Task.CompletedTask; } public Task Rollback() { _operations.Clear(); return Task.CompletedTask; } public void Dispose() { _operations.Clear(); } } ``` - [ ] **Step 3: 创建内存事务实现** ```csharp using WIDESEAWCS_Application.Common; namespace WIDESEAWCS_Infrastructure.Persistence; public class InMemoryTransaction : ITransaction { private readonly InMemoryUnitOfWork _unitOfWork; private bool _committed; private bool _rolledBack; public InMemoryTransaction(InMemoryUnitOfWork unitOfWork) { _unitOfWork = unitOfWork; } public Task Commit() { if (_rolledBack) throw new InvalidOperationException("Transaction already rolled back"); _committed = true; return Task.CompletedTask; } public Task Rollback() { if (_committed) throw new InvalidOperationException("Transaction already committed"); _rolledBack = true; return _unitOfWork.Rollback(); } public void Dispose() { if (!_committed && !_rolledBack) { Rollback().GetAwaiter().GetResult(); } } } ``` - [ ] **Step 4: 提交基础设施层基础实现** ```bash git add WIDESEAWCS_Infrastructure/ git commit -m "feat: 添加基础设施层基础实现 - InMemoryUnitOfWork, InMemoryTransaction" ``` --- ## Chunk 2: 设备管理领域实现 ### Task 4: 实现设备值对象 **Files:** - Create: `WIDESEAWCS_Domain/DeviceManagement/ValueObjects/DeviceId.cs` - Create: `WIDESEAWCS_Domain/DeviceManagement/ValueObjects/DeviceName.cs` - Create: `WIDESEAWCS_Domain/DeviceManagement/ValueObjects/DeviceAddress.cs` - Create: `WIDESEAWCS_Domain/DeviceManagement/ValueObjects/DeviceType.cs` - Create: `WIDESEAWCS_Domain/DeviceManagement/ValueObjects/DeviceStatus.cs` - Test: `WIDESEAWCS_Domain.Tests/DeviceManagement/ValueObjects/DeviceValueObjectsTests.cs` - [ ] **Step 1: 创建设备ID值对象** ```csharp namespace WIDESEAWCS_Domain.DeviceManagement.ValueObjects; public record DeviceId(Guid Value) { public static DeviceId New() => new DeviceId(Guid.NewGuid()); public static DeviceId From(Guid value) => new DeviceId(value); } ``` - [ ] **Step 2: 创建设备名称值对象** ```csharp namespace WIDESEAWCS_Domain.DeviceManagement.ValueObjects; public record DeviceName(string Value) { public DeviceName(string value) { if (string.IsNullOrWhiteSpace(value)) throw new ArgumentException("设备名称不能为空", nameof(value)); if (value.Length > 100) throw new ArgumentException("设备名称长度不能超过100个字符", nameof(value)); Value = value; } } ``` - [ ] **Step 3: 创建设备地址值对象** ```csharp using System.Net; namespace WIDESEAWCS_Domain.DeviceManagement.ValueObjects; public record DeviceAddress(string Ip, int Port) { public DeviceAddress(string ip, int port) { if (!IPAddress.TryParse(ip, out _)) throw new ArgumentException("IP地址格式无效", nameof(ip)); if (port < 1 || port > 65535) throw new ArgumentException("端口号必须在1-65535之间", nameof(port)); Ip = ip; Port = port; } } ``` - [ ] **Step 4: 创建设备类型枚举** ```csharp namespace WIDESEAWCS_Domain.DeviceManagement.ValueObjects; public enum DeviceType { StackerCrane = 1, // 堆垛机 ConveyorLine = 2, // 输送线 ShuttleCar = 3, // 穿梭车 Robot = 4, // 机械手 AGV = 5 // 自动导引车 } ``` - [ ] **Step 5: 创建设备状态枚举** ```csharp namespace WIDESEAWCS_Domain.DeviceManagement.ValueObjects; public enum DeviceStatus { Disconnected = 0, // 未连接 Connecting = 1, // 连接中 Connected = 2, // 已连接 Busy = 3, // 忙碌 Error = 4, // 错误 Maintenance = 5 // 维护中 } ``` - [ ] **Step 6: 提交设备值对象** ```bash git add WIDESEAWCS_Domain/DeviceManagement/ValueObjects/ git commit -m "feat: 添加设备值对象 - DeviceId, DeviceName, DeviceAddress, DeviceType, DeviceStatus" ``` --- ### Task 5: 实现设备聚合根 **Files:** - Create: `WIDESEAWCS_Domain/DeviceManagement/Aggregates/Device.cs` - Create: `WIDESEAWCS_Domain/DeviceManagement/Events/DeviceConnectedEvent.cs` - Create: `WIDESEAWCS_Domain/DeviceManagement/Events/DeviceDisconnectedEvent.cs` - Create: `WIDESEAWCS_Domain/DeviceManagement/Events/DeviceHeartbeatEvent.cs` - Test: `WIDESEAWCS_Domain.Tests/DeviceManagement/Aggregates/DeviceTests.cs` - [ ] **Step 1: 创建设备连接事件** ```csharp using WIDESEAWCS_Domain.Common; using WIDESEAWCS_Domain.DeviceManagement.ValueObjects; namespace WIDESEAWCS_Domain.DeviceManagement.Events; public record DeviceConnectedEvent : DomainEvent { public DeviceId DeviceId { get; init; } public DateTime ConnectedAt { get; init; } = DateTime.UtcNow; } ``` - [ ] **Step 2: 创建设备断开事件** ```csharp using WIDESEAWCS_Domain.Common; using WIDESEAWCS_Domain.DeviceManagement.ValueObjects; namespace WIDESEAWCS_Domain.DeviceManagement.Events; public record DeviceDisconnectedEvent : DomainEvent { public DeviceId DeviceId { get; init; } public string Reason { get; init; } = string.Empty; } ``` - [ ] **Step 3: 创建设备心跳事件** ```csharp using WIDESEAWCS_Domain.Common; using WIDESEAWCS_Domain.DeviceManagement.ValueObjects; namespace WIDESEAWCS_Domain.DeviceManagement.Events; public record DeviceHeartbeatEvent : DomainEvent { public DeviceId DeviceId { get; init; } public DateTime HeartbeatAt { get; init; } = DateTime.UtcNow; } ``` - [ ] **Step 4: 创建设备聚合根类** ```csharp using WIDESEAWCS_Domain.Common; using WIDESEAWCS_Domain.DeviceManagement.Events; using WIDESEAWCS_Domain.DeviceManagement.ValueObjects; namespace WIDESEAW_Domain.DeviceManagement.Aggregates; public class Device : AggregateRoot { private DeviceId _id; private DeviceName _name; private DeviceType _type; private DeviceStatus _status; private DeviceAddress? _address; private DateTime _lastConnectedAt; private DateTime _lastHeartbeatAt; private string? _errorMessage; public Device(DeviceId id, DeviceName name, DeviceType type) { _id = id; _name = name; _type = type; _status = DeviceStatus.Disconnected; _lastConnectedAt = DateTime.MinValue; _lastHeartbeatAt = DateTime.MinValue; } // 公共属性访问器 public DeviceId Id => _id; public DeviceName Name => _name; public DeviceType Type => _type; public DeviceStatus Status => _status; public DeviceAddress? Address => _address; public DateTime LastConnectedAt => _lastConnectedAt; public DateTime LastHeartbeatAt => _lastHeartbeatAt; public string? ErrorMessage => _errorMessage; // 行为方法 public void Connect() { if (_status == DeviceStatus.Connected) throw new InvalidDomainOperationException("设备已连接,无法重复连接"); _status = DeviceStatus.Connected; _lastConnectedAt = DateTime.UtcNow; _errorMessage = null; IncrementVersion(); AddDomainEvent(new DeviceConnectedEvent { DeviceId = _id, ConnectedAt = _lastConnectedAt }); } public void Disconnect(string reason) { if (_status == DeviceStatus.Disconnected) throw new InvalidDomainOperationException("设备已断开,无法重复断开"); _status = DeviceStatus.Disconnected; _errorMessage = reason; IncrementVersion(); AddDomainEvent(new DeviceDisconnectedEvent { DeviceId = _id, Reason = reason }); } public void UpdateHeartbeat() { if (_status != DeviceStatus.Connected && _status != DeviceStatus.Busy) throw new InvalidDomainOperationException("设备未连接或忙碌,无法更新心跳"); _lastHeartbeatAt = DateTime.UtcNow; IncrementVersion(); AddDomainEvent(new DeviceHeartbeatEvent { DeviceId = _id, HeartbeatAt = _lastHeartbeatAt }); } public void SetAddress(DeviceAddress address) { _address = address; IncrementVersion(); } // 内部方法(供领域服务使用) internal void SetStatus(DeviceStatus status) { _status = status; } internal void SetError(string errorMessage) { _status = DeviceStatus.Error; _errorMessage = errorMessage; IncrementVersion(); } } ``` - [ ] **Step 5: 提交设备聚合根实现** ```bash git add WIDESEAWCS_Domain/DeviceManagement/ git commit -m "feat: 实现设备聚合根和领域事件" ``` --- ### Task 6: 实现设备状态机 **Files:** - Create: `WIDESEAWCS_Domain/DeviceManagement/Services/IDeviceStateMachine.cs` - Create: `WIDESEAWCS_Domain/DeviceManagement/Services/DeviceStateMachine.cs` - [ ] **Step 1: 创建设备状态机接口** ```csharp using WIDESEAWCS_Domain.DeviceManagement.Aggregates; using WIDESEAWCS_Domain.DeviceManagement.ValueObjects; namespace WIDESEAWCS_Domain.DeviceManagement.Services; public interface IDeviceStateMachine { bool CanTransition(DeviceStatus current, DeviceStatus target); void Transition(Device device, DeviceStatus target); } ``` - [ ] **Step 2: 创建设备状态机实现** ```csharp using WIDESEAWCS_Domain.DeviceManagement.Aggregates; using WIDESEAWCS_Domain.DeviceManagement.ValueObjects; using WIDESEAWCS_Domain.Common; namespace WIDESEAWCS_Domain.DeviceManagement.Services; public class DeviceStateMachine : IDeviceStateMachine { private static readonly Dictionary> _transitions = new() { [DeviceStatus.Disconnected] = new() { DeviceStatus.Connecting, DeviceStatus.Maintenance }, [DeviceStatus.Connecting] = new() { DeviceStatus.Connected, DeviceStatus.Error }, [DeviceStatus.Connected] = new() { DeviceStatus.Busy, DeviceStatus.Disconnected, DeviceStatus.Error, DeviceStatus.Maintenance }, [DeviceStatus.Busy] = new() { DeviceStatus.Connected, DeviceStatus.Error }, [DeviceStatus.Error] = new() { DeviceStatus.Disconnected, DeviceStatus.Maintenance }, [DeviceStatus.Maintenance] = new() { DeviceStatus.Disconnected } }; public bool CanTransition(DeviceStatus current, DeviceStatus target) { if (!_transitions.TryGetValue(current, out var allowed)) return false; return allowed.Contains(target); } public void Transition(Device device, DeviceStatus target) { if (!CanTransition(device.Status, target)) { throw new InvalidDomainOperationException( $"无效的状态转换: {device.Status} -> {target}"); } device.SetStatus(target); } } ``` - [ ] **Step 3: 提交设备状态机实现** ```bash git add WIDESEAWCS_Domain/DeviceManagement/Services/ git commit -m "feat: 实现设备状态机领域服务" ``` --- ### Task 7: 实现设备仓储接口和实现 **Files:** - Create: `WIDESEAWCS_Application/DeviceManagement/Repositories/IDeviceRepository.cs` - Create: `WIDESEAWCS_Infrastructure/Persistence/Repositories/InMemoryDeviceRepository.cs` - [ ] **Step 1: 创建设备仓储接口** ```csharp using WIDESEAWCS_Application.Common; using WIDESEAWCS_Domain.DeviceManagement.Aggregates; using WIDESEAWCS_Domain.DeviceManagement.ValueObjects; namespace WIDESEAWCS_Application.DeviceManagement.Repositories; public interface IDeviceRepository : IRepository { Task GetByName(DeviceName name); Task> GetByType(DeviceType type); Task> GetByStatus(DeviceStatus status); Task> GetAllConnected(); } ``` - [ ] **Step 2: 创建内存设备仓储实现** ```csharp using WIDESEAWCS_Application.Common; using WIDESEAWCS_Application.DeviceManagement.Repositories; using WIDESEAWCS_Domain.DeviceManagement.Aggregates; using WIDESEAWCS_Domain.DeviceManagement.ValueObjects; namespace WIDESEAWCS_Infrastructure.Persistence.Repositories; public class InMemoryDeviceRepository : IDeviceRepository { private readonly Dictionary _devices = new(); public Task GetById(DeviceId id) { _devices.TryGetValue(id, out var device); return Task.FromResult(device); } public Task Add(Device entity) { _devices[entity.Id] = entity; return Task.CompletedTask; } public Task Update(Device entity) { _devices[entity.Id] = entity; return Task.CompletedTask; } public Task Delete(DeviceId id) { _devices.Remove(id); return Task.CompletedTask; } public async Task GetByName(DeviceName name) { var device = _devices.Values.FirstOrDefault(d => d.Name == name); return Task.FromResult(device); } public async Task> GetByType(DeviceType type) { var devices = _devices.Values.Where(d => d.Type == type); return Task.FromResult(devices); } public async Task> GetByStatus(DeviceStatus status) { var devices = _devices.Values.Where(d => d.Status == status); return Task.FromResult(devices); } public async Task> GetAllConnected() { var devices = _devices.Values.Where(d => d.Status == DeviceStatus.Connected); return Task.FromResult(devices); } } ``` - [ ] **Step 3: 提交设备仓储实现** ```bash git add WIDESEAWCS_Application/DeviceManagement/Repositories/ WIDESEAWCS_Infrastructure/Persistence/Repositories/ git commit -m "feat: 实现设备仓储接口和内存实现" ``` --- ## Chunk 3: 应用服务实现 ### Task 8: 实现设备应用服务 **Files:** - Create: `WIDESEAWCS_Application/DeviceManagement/Services/DeviceApplicationService.cs` - Create: `WIDESEAWCS_Application/DeviceManagement/DTOs/DeviceDto.cs` - [ ] **Step 1: 创建设备DTO** ```csharp using WIDESEAWCS_Domain.DeviceManagement.ValueObjects; namespace WIDESEAWCS_Application.DeviceManagement.DTOs; public record DeviceDto { public Guid Id { get; init; } public string Name { get; init; } = string.Empty; public DeviceType Type { get; init; } public DeviceStatus Status { get; init; } public string? Ip { get; init; } public int? Port { get; init; } public DateTime LastConnectedAt { get; init; } public DateTime LastHeartbeatAt { get; init; } public string? ErrorMessage { get; init; } } ``` - [ ] **Step 2: 创建设备应用服务** ```csharp using WIDESEAWCS_Application.Common; using WIDESEAWCS_Application.DeviceManagement.DTOs; using WIDESEAWCS_Application.DeviceManagement.Repositories; using WIDESEAWCS_Domain.DeviceManagement.Aggregates; using WIDESEAWCS_Domain.DeviceManagement.ValueObjects; using WIDESEAWCS_Domain.Common; namespace WIDESEAWCS_Application.DeviceManagement.Services; public class DeviceApplicationService { private readonly IDeviceRepository _deviceRepository; private readonly IUnitOfWork _unitOfWork; public DeviceApplicationService( IDeviceRepository deviceRepository, IUnitOfWork unitOfWork) { _deviceRepository = deviceRepository; _unitOfWork = unitOfWork; } public async Task GetDevice(Guid id) { var deviceId = DeviceId.From(id); var device = await _deviceRepository.GetById(deviceId); return device?.ToDto(); } public async Task CreateDevice(string name, DeviceType type, string ip, int port) { await using var transaction = _unitOfWork.BeginTransaction(); var deviceId = DeviceId.New(); var deviceName = new DeviceName(name); var device = new Device(deviceId, deviceName, type); device.SetAddress(new DeviceAddress(ip, port)); await _deviceRepository.Add(device); await transaction.Commit(); await _unitOfWork.Commit(); return device.ToDto(); } public async Task ConnectDevice(Guid id) { var deviceId = DeviceId.From(id); var device = await _deviceRepository.GetById(deviceId); if (device == null) throw new DomainException($"设备不存在: {id}", "DEVICE_NOT_FOUND"); device.Connect(); await _deviceRepository.Update(device); } public async Task DisconnectDevice(Guid id, string reason) { var deviceId = DeviceId.From(id); var device = await _deviceRepository.GetById(deviceId); if (device == null) throw new DomainException($"设备不存在: {id}", "DEVICE_NOT_FOUND"); device.Disconnect(reason); await _deviceRepository.Update(device); } public async Task> GetConnectedDevices() { var devices = await _deviceRepository.GetAllConnected(); return devices.Select(d => d.ToDto()); } } ``` - [ ] **Step 3: 在Device类中添加ToDto扩展方法** ```csharp // 在 WIDESEAWCS_Domain/DeviceManagement/Aggregates/DeviceExtensions.cs 中创建 using WIDESEAWCS_Application.DeviceManagement.DTOs; namespace WIDESEAWCS_Domain.DeviceManagement.Aggregates; public static class DeviceExtensions { public static DeviceDto ToDto(this Device device) { return new DeviceDto { Id = device.Id.Value, Name = device.Name.Value, Type = device.Type, Status = device.Status, Ip = device.Address?.Ip, Port = device.Address?.Port, LastConnectedAt = device.LastConnectedAt, LastHeartbeatAt = device.LastHeartbeatAt, ErrorMessage = device.ErrorMessage }; } } ``` - [ ] **Step 4: 提交设备应用服务实现** ```bash git add WIDESEAWCS_Application/DeviceManagement/ WIDESEAWCS_Domain/DeviceManagement/Aggregates/DeviceExtensions.cs git commit -m "feat: 实现设备应用服务和DTO" ``` --- ## Chunk 4: 集成和验证 ### Task 9: 配置依赖注入 **Files:** - Create: `WIDESEAWCS_Infrastructure/DependencyInjection/ServiceCollectionExtensions.cs` - [ ] **Step 1: 创建服务注册扩展** ```csharp using Microsoft.Extensions.DependencyInjection; using WIDESEAWCS_Application.Common; using WIDESEAWCS_Application.DeviceManagement.Repositories; using WIDESEAWCS_Application.DeviceManagement.Services; using WIDESEAWCS_Infrastructure.Persistence; using WIDESEAWCS_Infrastructure.Persistence.Repositories; using WIDESEAWCS_Domain.DeviceManagement.Services; namespace WIDESEAWCS_Infrastructure.DependencyInjection; public static class ServiceCollectionExtensions { public static IServiceCollection AddDeviceManagementServices(this IServiceCollection services) { // 注册领域服务 services.AddSingleton(); // 注册仓储 services.AddSingleton(); // 注册工作单元 services.AddSingleton(); // 注册应用服务 services.AddScoped(); return services; } } ``` - [ ] **Step 2: 提交依赖注入配置** ```bash git add WIDESEAWCS_Infrastructure/DependencyInjection/ git commit -m "feat: 配置设备管理服务依赖注入" ``` --- ### Task 10: 集成测试 **Files:** - Create: `WIDESEAWCS_IntegrationTests/DeviceManagement/DeviceIntegrationTests.cs` - [ ] **Step 1: 创建集成测试** ```csharp using Xunit; using Microsoft.Extensions.DependencyInjection; using WIDESEAWCS_Application.DeviceManagement.Services; using WIDESEAWCS_Domain.DeviceManagement.Aggregates; using WIDESEAWCS_Domain.DeviceManagement.ValueObjects; using WIDESEAWCS_Infrastructure.DependencyInjection; namespace WIDESEAWCS_IntegrationTests.DeviceManagement; public class DeviceIntegrationTests { private readonly IServiceProvider _serviceProvider; private readonly DeviceApplicationService _service; public DeviceIntegrationTests() { var services = new ServiceCollection(); services.AddDeviceManagementServices(); _serviceProvider = services.BuildServiceProvider(); _service = _serviceProvider.GetRequiredService(); } [Fact] public async Task DeviceWorkflow_Should_CompleteSuccessfully() { // Arrange & Act - 创建设备 var device = await _service.CreateDevice("IntegrationTestDevice", DeviceType.StackerCrane, "127.0.0.1", 8080); Assert.NotEqual(Guid.Empty, device.Id); Assert.Equal(DeviceType.StackerCrane, device.Type); Assert.Equal("IntegrationTestDevice", device.Name.Value); Assert.Equal(DeviceStatus.Disconnected, device.Status); // Act - 连接设备 await _service.ConnectDevice(device.Id); var connectedDevice = await _service.GetDevice(device.Id); Assert.NotNull(connectedDevice); Assert.Equal(DeviceStatus.Connected, connectedDevice!.Status); // Act - 断开设备 await _service.DisconnectDevice(device.Id, "Integration test disconnect"); var disconnectedDevice = await _service.GetDevice(device.Id); Assert.NotNull(disconnectedDevice); Assert.Equal(DeviceStatus.Disconnected, disconnectedDevice!.Status); Assert.Equal("Integration test disconnect", disconnectedDevice!.ErrorMessage); } // Act - 获取已连接设备 var connectedDevices = await _service.GetConnectedDevices(); Assert.Single(connectedDevices); Assert.Equal("IntegrationTestDevice", connectedDevices.First().Name); } [Fact] public async Task GetConnectedDevices_Should_ReturnOnlyConnected() { // Arrange & Act - 创建多个设备 var device1 = await _service.CreateDevice("Device1", DeviceType.ConveyorLine, "127.0.0.2", 8081); var device2 = await _service.CreateDevice("Device2", DeviceType.ShuttleCar, "127.0.0.3", 8082); // Act - 只连接第一个设备 await _service.ConnectDevice(device1.Id); var connectedDevices = await _service.GetConnectedDevices(); Assert.Single(connectedDevices); Assert.Equal("Device1", connectedDevices.First().Name); } } ``` - [ ] **Step 2: 提交集成测试** ```bash git add WIDESEAWCS_IntegrationTests/DeviceManagement/ git commit -m "test: 添加设备管理集成测试" ``` --- ### Task 11: 更新文档 **Files:** - Modify: `CLAUDE.md` - [ ] **Step 1: 更新CLAUDE.md添加DDD架构说明** 在CLAUDE.md中添加: ```markdown ## DDD架构实施 项目采用渐进式DDD架构,包含以下层次: ### 领域层 (WIDESEAWCS_Domain) - 聚合根:Device, DeviceTask等 - 值对象:DeviceId, DeviceName, DeviceAddress等 - 领域服务:DeviceStateMachine, DeviceScheduler等 - 领域事件:DeviceConnectedEvent, TaskCompletedEvent等 ### 应用层 (WIDESEAWCS_Application) - 应用服务:DeviceApplicationService等 - DTOs:设备、任务等数据传输对象 - 仓储接口:IDeviceRepository, ITaskRepository等 - 工作单元:IUnitOfWork ### 基础设施层 (WIDESEAWCS_Infrastructure) - 仓储实现:InMemoryDeviceRepository, DeviceRepository等 - 数据持久化:SqlSugar集成 - 缓存:Redis缓存实现 - 通信:设备通信适配器 ### 特性开关 支持新旧代码共存,可通过配置控制: ```json "FeatureFlags": { "UseNewDeviceManagement": true, "UseNewTaskScheduler": false } ``` ``` - [ ] **Step 2: 提交文档更新** ```bash git add CLAUDE.md git commit -m "docs: 更新CLAUDE.md添加DDD架构说明" ``` --- ### Task 12: 总结和后续步骤 - [ ] **Step 1: 查看所有提交** ```bash git log --oneline --graph ``` - [ ] **Step 2: 显示总结信息** 本次实施计划已完成阶段1-4,共11个任务: **已完成的工作**: 1. 基础设施搭建:DDD基础类、应用层接口、基础设施层实现 2. 设备管理领域:设备值对象、聚合根、状态机、仓储 3. 应用层实现:设备应用服务、DTO、扩展方法 4. 集成验证:依赖注入配置、集成测试 5. 文档更新:CLAUDE.md **下一步建议**: 1. 编写完整的单元测试和集成测试 2. 实现SqlSugar仓储实现(替换内存实现) 3. 实现缓存优化(Redis集成) 4. 配置特性开关支持新旧代码共存 5. 编写API控制器(DeviceController) 6. 进行性能测试和优化 7. 创建Pull Request合并到master分支