wanshenmean
2026-03-11 b54877f2400f752f5b9a1c8aef39c4aa9e851ac5
docs: 添加WCS系统DDD重构设计文档
已添加1个文件
840 ■■■■■ 文件已修改
Code/WCS/WIDESEAWCS_Server/docs/superpowers/specs/2026-03-11-wcs-ddd-refactor-design.md 840 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Code/WCS/WIDESEAWCS_Server/docs/superpowers/specs/2026-03-11-wcs-ddd-refactor-design.md
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,840 @@
# WCS系统渐进式DDD重构设计文档
**创建日期**: 2026-03-11
**版本**: 1.0
**范围**: å…¨ç³»ç»Ÿé‡æž„(渐进式DDD)
## 1. æž¶æž„总览
基于您的需求,我将提出一个适合2周时间窗口的渐进式DDD重构方案。
```
┌─────────────────────────────────────────────────────────────┐
│                        è¡¨çް层 (API)                           â”‚
│  WIDESEAWCS_Server (ASP.NET Core Controllers)                â”‚
└─────────────────────────┬───────────────────────────────────┘
                          â”‚
                          â–¼
┌─────────────────────────────────────────────────────────────┐
│                        åº”用层                                 â”‚
│  åº”用服务 / DTO / å‘½ä»¤ / æŸ¥è¯¢                                  â”‚
└─────────────────────────┬───────────────────────────────────┘
                          â”‚
                          â–¼
┌─────────────────────────────────────────────────────────────┐
│                        é¢†åŸŸå±‚ (新增)                          â”‚
│  â”Œâ”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”   â”‚
│  â”‚  è®¾å¤‡ç®¡ç†é¢†åŸŸ                                          â”‚   â”‚
│  â”‚  â€¢ èšåˆæ ¹: è®¾å¤‡, è®¾å¤‡ç»„, è®¾å¤‡ä»»åŠ¡                     â”‚   â”‚
│  â”‚  â€¢ å®žä½“: è®¾å¤‡çŠ¶æ€, è®¾å¤‡ä½ç½®, è®¾å¤‡å±žæ€§                 â”‚   â”‚
│  â”‚  â€¢ å€¼å¯¹è±¡: è®¾å¤‡ID, åœ°å€, çŠ¶æ€æžšä¸¾                    â”‚   â”‚
│  â”‚  â€¢ é¢†åŸŸæœåŠ¡: è®¾å¤‡è°ƒåº¦å™¨, çŠ¶æ€æœº                      â”‚   â”‚
│  â”‚  â€¢ ä»“储接口: IDeviceRepository, IDeviceTaskRepository â”‚   â”‚
│  â””───────────────────────────────────────────────────────┘   â”‚
│  â”Œâ”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”   â”‚
│  â”‚  ä»“储作业领域                                          â”‚   â”‚
│  â”‚  â€¢ èšåˆæ ¹: ä½œä¸š, æ‰˜ç›˜, åº“位                            â”‚   â”‚
│  â”‚  â€¢ é¢†åŸŸäº‹ä»¶: ä½œä¸šå®Œæˆ, ä½œä¸šå¼‚常                        â”‚   â”‚
│  â””───────────────────────────────────────────────────────┘   â”‚
│  â”Œâ”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”   â”‚
│  â”‚  ç³»ç»Ÿé›†æˆé¢†åŸŸ                                          â”‚   â”‚
│  â”‚  â€¢ é›†æˆæœåŠ¡: WMS集成, MES集成, ERP集成               â”‚   â”‚
│  â””───────────────────────────────────────────────────────┘   â”‚
└─────────────────────────┬───────────────────────────────────┘
                          â”‚
                          â–¼
┌─────────────────────────────────────────────────────────────┐
│                      åŸºç¡€è®¾æ–½å±‚                              â”‚
│  â€¢ ä»“储实现 (SqlSugar ORM)                                   â”‚
│  â€¢ é€šä¿¡é©±åЍ (HslCommunication)                               â”‚
│  â€¢ ç¼“存服务 (Redis)                                          â”‚
│  â€¢ ä»»åŠ¡è°ƒåº¦ (Quartz.NET)                                     â”‚
│  â€¢ æ•°æ®åº“访问 (SqlSugar)                                     â”‚
└─────────────────────────────────────────────────────────────┘
```
---
## 2. é¢†åŸŸå±‚设计 - è®¾å¤‡ç®¡ç†é¢†åŸŸï¼ˆæ ¸å¿ƒï¼‰
### 2.1 èšåˆæ ¹è®¾è®¡
```csharp
// è®¾å¤‡èšåˆæ ¹
public class Device : AggregateRoot<DeviceId>
{
    // åŸºæœ¬å±žæ€§
    private DeviceId _id;
    private DeviceName _name;
    private DeviceType _type;
    private DeviceStatus _status;
    private DeviceAddress _address;
    private List<DeviceProperty> _properties;
    // çŠ¶æ€ç®¡ç†
    private DateTime _lastConnectedAt;
    private DateTime _lastHeartbeatAt;
    private string _errorMessage;
    // é¢†åŸŸäº‹ä»¶
    private List<IDomainEvent> _domainEvents = new();
    // è¡Œä¸ºæ–¹æ³•
    public void Connect()
    {
        if (_status == DeviceStatus.Connected)
            throw new DomainException("设备已连接");
        _status = DeviceStatus.Connected;
        _lastConnectedAt = DateTime.UtcNow;
        _domainEvents.Add(new DeviceConnectedEvent(_id));
    }
    public void Disconnect(string reason)
    {
        _status = DeviceStatus.Disconnected;
        _errorMessage = reason;
        _domainEvents.Add(new DeviceDisconnectedEvent(_id, reason));
    }
    public void UpdateHeartbeat()
    {
        _lastHeartbeatAt = DateTime.UtcNow;
        _domainEvents.Add(new DeviceHeartbeatEvent(_id));
    }
    public void SetProperty(string key, string value)
    {
        var property = _properties.FirstOrDefault(p => p.Key == key);
        if (property != null)
            property.UpdateValue(value);
        else
            _properties.Add(DeviceProperty.Create(key, value));
    }
    public IReadOnlyCollection<IDomainEvent> GetDomainEvents() => _domainEvents.AsReadOnly();
    public void ClearDomainEvents() => _domainEvents.Clear();
}
// è®¾å¤‡ç»„聚合根
public class DeviceGroup : AggregateRoot<DeviceGroupId>
{
    private DeviceGroupId _id;
    private DeviceGroupName _name;
    private List<DeviceId> _deviceIds;
    private GroupStrategy _strategy;
    private int _currentIndex;
    public void AddDevice(DeviceId deviceId)
    {
        if (_deviceIds.Contains(deviceId))
            throw new DomainException("设备已在组内");
        _deviceIds.Add(deviceId);
    }
    public void RemoveDevice(DeviceId deviceId)
    {
        _deviceIds.Remove(deviceId);
    }
    // è½®è¯¢ç­–ç•¥
    public DeviceId GetNextDevice()
    {
        if (!_deviceIds.Any())
            throw new DomainException("设备组为空");
        switch (_strategy)
        {
            case GroupStrategy.RoundRobin:
                return _deviceIds[_currentIndex++ % _deviceIds.Count];
            case GroupStrategy.Random:
                return _deviceIds[new Random().Next(_deviceIds.Count)];
            default:
                return _deviceIds[0];
        }
    }
}
// è®¾å¤‡ä»»åŠ¡èšåˆæ ¹
public class DeviceTask : AggregateRoot<DeviceTaskId>
{
    private DeviceTaskId _id;
    private DeviceId _deviceId;
    private TaskType _type;
    private TaskStatus _status;
    private TaskPriority _priority;
    private TaskPayload _payload;
    private DateTime _createdAt;
    private DateTime? _startedAt;
    private DateTime? _completedAt;
    private string? _errorMessage;
    private List<IDomainEvent> _domainEvents = new();
    public void Start()
    {
        if (_status != TaskStatus.Pending)
            throw new DomainException("任务状态不正确");
        _status = TaskStatus.Running;
        _startedAt = DateTime.UtcNow;
        _domainEvents.Add(new TaskStartedEvent(_id, _deviceId));
    }
    public void Complete()
    {
        if (_status != TaskStatus.Running)
            throw new DomainException("任务未在运行中");
        _status = TaskStatus.Completed;
        _completedAt = DateTime.UtcNow;
        _domainEvents.Add(new TaskCompletedEvent(_id, _deviceId));
    }
    public void Fail(string errorMessage)
    {
        _status = TaskStatus.Failed;
        _errorMessage = errorMessage;
        _completedAt = DateTime.UtcNow;
        _domainEvents.Add(new TaskFailedEvent(_id, _deviceId, errorMessage));
    }
    public IReadOnlyCollection<IDomainEvent> GetDomainEvents() => _domainEvents.AsReadOnly();
    public void ClearDomainEvents() => _domainEvents.Clear();
}
```
### 2.2 å€¼å¯¹è±¡è®¾è®¡
```csharp
// è®¾å¤‡ID值对象
public record DeviceId(Guid Value)
{
    public static DeviceId New() => new DeviceId(Guid.NewGuid());
    public static DeviceId From(Guid value) => new DeviceId(value);
}
// è®¾å¤‡åç§°å€¼å¯¹è±¡
public record DeviceName(string Value)
{
    public DeviceName(string value)
    {
        if (string.IsNullOrWhiteSpace(value) || value.Length > 100)
            throw new ArgumentException("设备名称无效");
        Value = value;
    }
}
// è®¾å¤‡ç±»åž‹æžšä¸¾
public enum DeviceType
{
    StackerCrane,    // å †åž›æœº
    ConveyorLine,    // è¾“送线
    ShuttleCar,      // ç©¿æ¢­è½¦
    Robot,           // æœºæ¢°æ‰‹
    AGV              // è‡ªåŠ¨å¯¼å¼•è½¦
}
// è®¾å¤‡çŠ¶æ€æžšä¸¾
public enum DeviceStatus
{
    Disconnected,    // æœªè¿žæŽ¥
    Connecting,      // è¿žæŽ¥ä¸­
    Connected,       // å·²è¿žæŽ¥
    Busy,            // å¿™ç¢Œ
    Error,           // é”™è¯¯
    Maintenance      // ç»´æŠ¤ä¸­
}
// è®¾å¤‡åœ°å€å€¼å¯¹è±¡
public record DeviceAddress(string Ip, int Port)
{
    public DeviceAddress(string ip, int port)
    {
        if (!IPAddress.TryParse(ip, out _))
            throw new ArgumentException("IP地址无效");
        if (port < 1 || port > 65535)
            throw new ArgumentException("端口无效");
        Ip = ip;
        Port = port;
    }
}
```
### 2.3 é¢†åŸŸæœåŠ¡è®¾è®¡
```csharp
// è®¾å¤‡è°ƒåº¦é¢†åŸŸæœåŠ¡
public interface IDeviceScheduler
{
    Task<DeviceTask> CreateTask(DeviceId deviceId, TaskType type, TaskPayload payload);
    Task<DeviceTask> AssignTask(DeviceId deviceId);
    Task CompleteTask(DeviceTaskId taskId);
    Task FailTask(DeviceTaskId taskId, string errorMessage);
}
public class DeviceScheduler : IDeviceScheduler
{
    private readonly IDeviceTaskRepository _taskRepository;
    private readonly IDeviceRepository _deviceRepository;
    private readonly ITaskQueue _taskQueue;
    public async Task<DeviceTask> CreateTask(DeviceId deviceId, TaskType type, TaskPayload payload)
    {
        var device = await _deviceRepository.GetById(deviceId);
        if (device == null)
            throw new DomainException("设备不存在");
        if (device.Status != DeviceStatus.Connected)
            throw new DomainException("设备未连接");
        var task = DeviceTask.Create(deviceId, type, payload);
        await _taskRepository.Add(task);
        await _taskQueue.Enqueue(task);
        return task;
    }
    public async Task<DeviceTask> AssignTask(DeviceId deviceId)
    {
        var tasks = await _taskRepository.GetPendingTasksForDevice(deviceId);
        if (!tasks.Any())
            return null;
        var task = tasks.OrderBy(t => t.Priority).First();
        task.Start();
        await _taskRepository.Update(task);
        return task;
    }
}
// è®¾å¤‡çŠ¶æ€æœºé¢†åŸŸæœåŠ¡
public interface IDeviceStateMachine
{
    bool CanTransition(DeviceStatus current, DeviceStatus target);
    void Transition(Device device, DeviceStatus target);
}
public class DeviceStateMachine : IDeviceStateMachine
{
    private static readonly Dictionary<DeviceStatus, HashSet<DeviceStatus>> _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)
    {
        return _transitions.TryGetValue(current, out var allowed) && allowed.Contains(target);
    }
    public void Transition(Device device, DeviceStatus target)
    {
        if (!CanTransition(device.Status, target))
            throw new DomainException($"无效的状态转换: {device.Status} -> {target}");
        device.SetStatus(target);
    }
}
```
### 2.4 ä»“储接口设计
```csharp
// è®¾å¤‡ä»“储接口
public interface IDeviceRepository : IRepository<Device, DeviceId>
{
    Task<Device?> GetByName(DeviceName name);
    Task<IEnumerable<Device>> GetByType(DeviceType type);
    Task<IEnumerable<Device>> GetByStatus(DeviceStatus status);
    Task<IEnumerable<Device>> GetAllConnected();
}
// è®¾å¤‡ä»»åŠ¡ä»“å‚¨æŽ¥å£
public interface IDeviceTaskRepository : IRepository<DeviceTask, DeviceTaskId>
{
    Task<IEnumerable<DeviceTask>> GetPendingTasksForDevice(DeviceId deviceId);
    Task<IEnumerable<DeviceTask>> GetRunningTasksForDevice(DeviceId deviceId);
    Task<IEnumerable<DeviceTask>> GetTasksByStatus(TaskStatus status);
}
```
---
## 3. åº”用层设计
### 3.1 åº”用服务设计
```csharp
// è®¾å¤‡åº”用服务
public class DeviceApplicationService
{
    private readonly IDeviceRepository _deviceRepository;
    private readonly IDeviceScheduler _deviceScheduler;
    private readonly IUnitOfWork _unitOfWork;
    public async Task<DeviceDto> GetDevice(DeviceId id)
    {
        var device = await _deviceRepository.GetById(id);
        return device.ToDto();
    }
    public async Task<DeviceTaskDto> CreateTask(CreateDeviceTaskCommand command)
    {
        await using var transaction = await _unitOfWork.BeginTransaction();
        var deviceId = DeviceId.From(command.DeviceId);
        var task = await _deviceScheduler.CreateTask(
            deviceId,
            command.Type,
            new TaskPayload(command.Payload)
        );
        await _unitOfWork.Commit();
        return task.ToDto();
    }
    public async Task CompleteTask(CompleteDeviceTaskCommand command)
    {
        var taskId = DeviceTaskId.From(command.TaskId);
        await _deviceScheduler.CompleteTask(taskId);
    }
}
// å‘½ä»¤å’ŒæŸ¥è¯¢å¯¹è±¡
public record CreateDeviceTaskCommand(Guid DeviceId, TaskType Type, string Payload);
public record CompleteDeviceTaskCommand(Guid TaskId);
// DTO
public record DeviceDto(Guid Id, string Name, DeviceType Type, DeviceStatus Status);
public record DeviceTaskDto(Guid Id, Guid DeviceId, TaskType Type, TaskStatus Status);
```
---
## 4. åŸºç¡€è®¾æ–½å±‚重构
### 4.1 ä»“储实现
```csharp
// è®¾å¤‡ä»“储实现
public class DeviceRepository : IDeviceRepository
{
    private readonly ISqlSugarClient _db;
    private readonly ICacheService _cache;
    public DeviceRepository(ISqlSugarClient db, ICacheService cache)
    {
        _db = db;
        _cache = cache;
    }
    public async Task<Device?> GetById(DeviceId id)
    {
        // å…ˆæŸ¥ç¼“å­˜
        var cached = await _cache.Get<Device>($"device:{id.Value}");
        if (cached != null)
            return cached;
        // æŸ¥æ•°æ®åº“
        var entity = await _db.Queryable<Dt_DeviceInfo>()
            .Where(d => d.DeviceId == id.Value)
            .FirstAsync();
        if (entity == null) return null;
        // è½¬æ¢ä¸ºé¢†åŸŸæ¨¡åž‹
        var device = MapToDomain(entity);
        // å†™å…¥ç¼“å­˜
        await _cache.Set($"device:{id.Value}", device, TimeSpan.FromMinutes(5));
        return device;
    }
    public async Task Add(Device device)
    {
        var entity = MapToEntity(device);
        await _db.Insertable(entity).ExecuteCommandAsync();
        // æ¸…除相关缓存
        await _cache.RemoveByPrefix("device:");
    }
    private Device MapToDomain(Dt_DeviceInfo entity) { /* ... */ }
    private Dt_DeviceInfo MapToEntity(Device domain) { /* ... */ }
}
```
### 4.2 é€šä¿¡é©±åŠ¨å°è£…
```csharp
// è®¾å¤‡é€šä¿¡é€‚配器接口
public interface IDeviceCommunicatorAdapter
{
    Task<bool> Connect(DeviceAddress address);
    Task<bool> Disconnect();
    Task<DeviceData> ReadData(string address);
    Task WriteData(string address, object value);
    Task<bool> IsConnected();
}
// Siemens PLC适配器
public class SiemensCommunicatorAdapter : IDeviceCommunicatorAdapter
{
    private readonly SiemensS7Net _plc;
    public SiemensCommunicatorAdapter(DeviceAddress address)
    {
        _plc = new SiemensS7Net(SiemensPLCS.S1200, address.Ip);
        _plc.ConnectTimeOut = 5000;
    }
    public async Task<bool> Connect(DeviceAddress address)
    {
        var result = await Task.Run(() => _plc.ConnectServer());
        return result.IsSuccess;
    }
    public async Task<DeviceData> ReadData(string address)
    {
        var result = await Task.Run(() => _plc.Read(address));
        if (!result.IsSuccess)
            throw new CommunicationException($"读取失败: {result.Message}");
        return new DeviceData(result.Content);
    }
    // ... å…¶ä»–方法
}
```
---
## 5. åˆ†å±‚清晰化
### 5.1 é¡¹ç›®ç»“构重组
```
WIDESEAWCS_Domain/
├── DeviceManagement/
│   â”œâ”€â”€ Aggregates/
│   â”‚   â”œâ”€â”€ Device.cs
│   â”‚   â”œâ”€â”€ DeviceGroup.cs
│   â”‚   â””── DeviceTask.cs
│   â”œâ”€â”€ Entities/
│   â”‚   â”œâ”€â”€ DeviceStatus.cs
│   â”‚   â”œâ”€â”€ DeviceLocation.cs
│   â”‚   â””── DeviceProperty.cs
│   â”œâ”€â”€ ValueObjects/
│   â”‚   â”œâ”€â”€ DeviceId.cs
│   â”‚   â”œâ”€â”€ DeviceName.cs
│   â”‚   â”œâ”€â”€ DeviceAddress.cs
│   â”‚   â””── TaskPayload.cs
│   â”œâ”€â”€ Services/
│   â”‚   â”œâ”€â”€ IDeviceScheduler.cs
│   â”‚   â”œâ”€â”€ DeviceScheduler.cs
│   â”‚   â”œâ”€â”€ IDeviceStateMachine.cs
│   â”‚   â””── DeviceStateMachine.cs
│   â”œâ”€â”€ Repositories/
│   â”‚   â”œâ”€â”€ IDeviceRepository.cs
│   â”‚   â””── IDeviceTaskRepository.cs
│   â””── Events/
│       â”œâ”€â”€ DeviceConnectedEvent.cs
│       â””── TaskCompletedEvent.cs
└── WarehouseOperations/
    â””── ...
WIDESEAWCS_Application/
├── Services/
│   â””── DeviceApplicationService.cs
├── Commands/
│   â””── CreateDeviceTaskCommand.cs
├── Queries/
│   â””── GetDeviceQuery.cs
└── DTOs/
    â””── DeviceDto.cs
WIDESEAWCS_Infrastructure/
├── Persistence/
│   â”œâ”€â”€ Repositories/
│   â”‚   â”œâ”€â”€ DeviceRepository.cs
│   â”‚   â””── DeviceTaskRepository.cs
│   â””── SqlSugar/
│       â””── DbContext.cs
├── Communication/
│   â”œâ”€â”€ Adapters/
│   â”‚   â”œâ”€â”€ SiemensCommunicatorAdapter.cs
│   â”‚   â”œâ”€â”€ OmronCommunicatorAdapter.cs
│   â”‚   â””── ModbusCommunicatorAdapter.cs
│   â””── IDeviceCommunicatorAdapter.cs
├── Caching/
│   â””── RedisCacheService.cs
└── Scheduling/
    â””── QuartzJobManager.cs
WIDESEAWCS_Server/
└── Controllers/
    â””── DeviceController.cs
```
---
## 6. æ€§èƒ½ä¼˜åŒ–ç­–ç•¥
### 6.1 ç¼“存优化
```csharp
// å¤šçº§ç¼“存策略
public class HybridCacheService : ICacheService
{
    private readonly IMemoryCache _l1Cache;
    private readonly IConnectionMultiplexer _redis;
    private readonly IDistributedCache _l2Cache;
    public async Task<T?> Get<T>(string key)
    {
        // L1缓存
        if (_l1Cache.TryGetValue(key, out T l1Value))
            return l1Value;
        // L2缓存
        var l2Value = await _l2Cache.GetStringAsync(key);
        if (l2Value != null)
        {
            var value = JsonSerializer.Deserialize<T>(l2Value);
            _l1Cache.Set(key, value, TimeSpan.FromMinutes(1));
            return value;
        }
        return default;
    }
    public async Task Set<T>(string key, T value, TimeSpan? expiry = null)
    {
        _l1Cache.Set(key, value, TimeSpan.FromMinutes(1));
        var serialized = JsonSerializer.Serialize(value);
        await _l2Cache.SetStringAsync(key, serialized, expiry);
    }
}
```
### 6.2 æ•°æ®åº“查询优化
```csharp
// æ‰¹é‡æŸ¥è¯¢ä¼˜åŒ–
public class DeviceRepository : IDeviceRepository
{
    public async Task<IDictionary<DeviceId, Device>> GetByIds(IEnumerable<DeviceId> ids)
    {
        var idList = ids.Select(id => id.Value).ToList();
        // æ‰¹é‡æŸ¥è¯¢
        var entities = await _db.Queryable<Dt_DeviceInfo>()
            .In(idList)
            .ToListAsync();
        return entities.ToDictionary(
            e => DeviceId.From(e.DeviceId),
            e => MapToDomain(e)
        );
    }
    // å»¶è¿ŸæŸ¥è¯¢
    public async Task<IEnumerable<Device>> QueryActiveDevices()
    {
        return await _db.Queryable<Dt_DeviceInfo>()
            .Where(d => d.Status != DeviceStatus.Disconnected)
            .OrderBy(d => d.Name)
            .Select(d => MapToDomain(d))
            .ToListAsync();
    }
}
```
---
## 7. é”™è¯¯å¤„理和日志统一
### 7.1 ç»Ÿä¸€å¼‚常处理
```csharp
// é¢†åŸŸå¼‚常基类
public abstract class DomainException : Exception
{
    public string ErrorCode { get; }
    protected DomainException(string message, string errorCode = "DOMAIN_ERROR")
        : base(message)
    {
        ErrorCode = errorCode;
    }
}
// åº”用异常
public class ApplicationException : Exception
{
    public string ErrorCode { get; }
    public ApplicationException(string message, string errorCode = "APP_ERROR")
        : base(message)
    {
        ErrorCode = errorCode;
    }
}
// å…¨å±€å¼‚常处理中间件
public class GlobalExceptionHandlerMiddleware
{
    public async Task InvokeAsync(HttpContext context, RequestDelegate next)
    {
        try
        {
            await next(context);
        }
        catch (DomainException ex)
        {
            context.Response.StatusCode = 400;
            await context.Response.WriteAsJsonAsync(new
            {
                error = ex.ErrorCode,
                message = ex.Message
            });
        }
        catch (ApplicationException ex)
        {
            context.Response.StatusCode = 500;
            await context.Response.WriteAsJsonAsync(new
            {
                error = ex.ErrorCode,
                message = ex.Message
            });
        }
    }
}
```
### 7.2 ç»“构化日志
```csharp
// é¢†åŸŸäº‹ä»¶æ—¥å¿—
public class DomainEventLogger : IDomainEventDispatcher
{
    private readonly ILogger<DomainEventLogger> _logger;
    public async Task Dispatch(IDomainEvent domainEvent)
    {
        _logger.LogInformation("领域事件: {EventType}, æ•°æ®: {EventData}",
            domainEvent.GetType().Name,
            JsonSerializer.Serialize(domainEvent));
        // å‘送到事件总线
    }
}
// è®¾å¤‡æ“ä½œæ—¥å¿—
public class DeviceOperationLogger : IDeviceOperationLogger
{
    public void LogConnect(DeviceId deviceId, bool success, string? error = null)
    {
        if (success)
        {
            _logger.LogInformation("设备连接成功: {DeviceId}", deviceId.Value);
        }
        else
        {
            _logger.LogError("设备连接失败: {DeviceId}, é”™è¯¯: {Error}",
                deviceId.Value, error);
        }
    }
}
```
---
## 8. å®žæ–½è®¡åˆ’(2周)
### ç¬¬1周:领域层搭建
| å¤©æ•° | ä»»åŠ¡ |
|------|------|
| Day 1-2 | åˆ›å»ºé¢†åŸŸé¡¹ç›®ç»“构,定义值对象和聚合根 |
| Day 3-4 | å®žçŽ°é¢†åŸŸæœåŠ¡å’ŒçŠ¶æ€æœº |
| Day 5 | åˆ›å»ºä»“储接口,定义领域事件 |
### ç¬¬2周:应用层和基础设施层
| å¤©æ•° | ä»»åŠ¡ |
|------|------|
| Day 1-2 | å®žçŽ°åº”ç”¨æœåŠ¡å’ŒDTO |
| Day 3 | åˆ›å»ºä»“储实现,迁移数据访问逻辑 |
| Day 4 | å°è£…通信驱动,优化缓存策略 |
| Day 5 | ç»Ÿä¸€é”™è¯¯å¤„理和日志,集成测试 |
---
## 9. è®¾è®¡å†³ç­–记录
### 9.1 ä¸ºä»€ä¹ˆé€‰æ‹©æ¸è¿›å¼DDD而非完整DDD重构?
**决策**:选择渐进式DDD重构而非完整DDD重构
**理由**:
1. æ—¶é—´çº¦æŸï¼š2周内无法完成完整DDD重构
2. é£Žé™©æŽ§åˆ¶ï¼šæ¸è¿›å¼é™ä½Žé£Žé™©ï¼Œå¯åˆ†é˜¶æ®µéªŒè¯
3. ç³»ç»Ÿç¨³å®šæ€§ï¼šä¿æŒçŽ°æœ‰åŠŸèƒ½å¯ç”¨
4. èµ„源效率:优先重构核心领域
### 9.2 ä¸ºä»€ä¹ˆå°†è®¾å¤‡ç®¡ç†ä½œä¸ºæ ¸å¿ƒé¢†åŸŸï¼Ÿ
**决策**:将设备管理作为第一个重构的核心领域
**理由**:
1. ä¸šåŠ¡ä¼˜å…ˆçº§ï¼šè®¾å¤‡ç®¡ç†æ˜¯WCS系统的核心
2. ä»£ç å¤æ‚度:设备管理包含状态、调度、通信等复杂逻辑
3. ä¼˜åŒ–空间大:设备管理存在较多性能和耦合问题
4. å½±å“èŒƒå›´å¹¿ï¼šè®¾å¤‡ç®¡ç†æ˜¯å…¶ä»–业务的基础
### 9.3 ä¸ºä»€ä¹ˆé‡‡ç”¨CQRS模式?
**决策**:在应用层采用CQRS模式
**理由**:
1. å…³æ³¨ç‚¹åˆ†ç¦»ï¼šå‘½ä»¤å’ŒæŸ¥è¯¢èŒè´£æ¸…æ™°
2. æ€§èƒ½ä¼˜åŒ–:可分别优化读写操作
3. æ‰©å±•性好:便于添加缓存、事件处理
---
## 10. éžåŠŸèƒ½æ€§éœ€æ±‚
### 10.1 æ€§èƒ½è¦æ±‚
- è®¾å¤‡çŠ¶æ€æŸ¥è¯¢å“åº”æ—¶é—´ < 100ms
- ä»»åŠ¡åˆ›å»ºå“åº”æ—¶é—´ < 200ms
- æ”¯æŒè‡³å°‘100个并发设备连接
### 10.2 å¯ç»´æŠ¤æ€§è¦æ±‚
- ä»£ç è¦†ç›–率 > 80%
- åœˆå¤æ‚度 < 10
- éµå¾ªå•一职责原则
### 10.3 å¯æ‰©å±•性要求
- æ”¯æŒæ–°å¢žè®¾å¤‡ç±»åž‹æ— éœ€ä¿®æ”¹æ ¸å¿ƒä»£ç 
- æ”¯æŒæ–°å¢žä»»åŠ¡ç±»åž‹
- æ”¯æŒå¤šç§é€šä¿¡åè®®æ‰©å±•
---
## 11. é£Žé™©è¯„ä¼°
| é£Žé™© | å¯èƒ½æ€§ | å½±å“ | ç¼“解措施 |
|------|--------|------|----------|
| æ—¶é—´ä¸è¶³ | é«˜ | é«˜ | åˆ†é˜¶æ®µå®žæ–½ï¼Œä¼˜å…ˆæ ¸å¿ƒåŠŸèƒ½ |
| æ•°æ®è¿ç§»é—®é¢˜ | ä¸­ | é«˜ | å……分测试,保留回滚方案 |
| æ€§èƒ½ä¸‹é™ | ä¸­ | ä¸­ | æ€§èƒ½åŸºå‡†æµ‹è¯•,监控优化 |
| å›¢é˜Ÿé€‚应 | ä½Ž | ä¸­ | æä¾›åŸ¹è®­ï¼Œæ–‡æ¡£å®Œå–„ |