zoukankan      html  css  js  c++  java
  • 框架设计

    项目分层以及依赖关系

    领域模型层

    基础设施层

    应用层

    共享层

    1、共享层

    XXX.Core:基础项目

    基础类型比如说异常或者帮助类等。不应该依赖任何项目。

    XXX.Domain.Abstractions:领域抽象层

    在领域模型中可以定义一些entity基类  接口  或者领域事件的接口等等。不应该依赖任何项目。

    XXX.Infrastructure.Core:基础设施核心层

    可以对我们的领域仓库还有我们EFContext定义一些基础共享的代码。

    依赖沃恩的领域模型抽象层,实现了我们的仓储

    2、领域模型层

    目的:专注于业务的设计,不依赖仓储等记住设置层。就是我们理解的实体层

    XXX.Domain

    里面会有不同的领域聚合,领域事件。

    领域对象需要继承领域抽象层,并实现一个聚合根的接口  表明他是一个聚合根。

    领域事件需要实现领域事件的接口

    3、基础设施层

    目的:他的仓库负责领域模型的存取,不负责任何业务代码
    XXX.Infrastructure

    就是我们实现的仓库(比如说 订单仓库等等)和一些共享代码的实现。

    数据库访问的实现:

    事务处理的实现:

    4、应用层

    可以分为两个

    XXX.API:接口

    XXX.BackgroundTasks:后台任务

    推荐:

    使用CQRS模式

    WEBAPI是面向前端交互的接口,避免依赖领域模型。

    领域模型设计

    分为领域模型的抽象层和领域模型的实现

    一、领域模型抽象层

     

     1、聚合根接口,实现他的表明是一个聚合根。

        public interface IAggregateRoot
        {
        }

    2、领域对象的接口

        public interface IEntity
        {
            object[] GetKeys();
        }
    
        public interface IEntity<TKey> : IEntity
        {
            TKey Id { get; }
        }

    3、领域对象的实现。里面定义了一些领域对象的公用方法。

        public abstract class Entity : IEntity
        {
            public abstract object[] GetKeys();
    
    
            public override string ToString()
            {
                return $"[Entity: {GetType().Name}] Keys = {string.Join(",", GetKeys())}";
            }
    
    
    
            #region 
            private List<IDomainEvent> _domainEvents;
            public IReadOnlyCollection<IDomainEvent> DomainEvents => _domainEvents?.AsReadOnly();
    
            public void AddDomainEvent(IDomainEvent eventItem)
            {
                _domainEvents = _domainEvents ?? new List<IDomainEvent>();
                _domainEvents.Add(eventItem);
            }
    
            public void RemoveDomainEvent(IDomainEvent eventItem)
            {
                _domainEvents?.Remove(eventItem);
            }
    
            public void ClearDomainEvents()
            {
                _domainEvents?.Clear();
            }
            #endregion
        }
    
    
        public abstract class Entity<TKey> : Entity, IEntity<TKey>
        {
            int? _requestedHashCode;
            public virtual TKey Id { get; protected set; }
            public override object[] GetKeys()
            {
                return new object[] { Id };
            }
            public override bool Equals(object obj)
            {
                if (obj == null || !(obj is Entity<TKey>))
                    return false;
    
                if (Object.ReferenceEquals(this, obj))
                    return true;
    
                if (this.GetType() != obj.GetType())
                    return false;
    
                Entity<TKey> item = (Entity<TKey>)obj;
    
                if (item.IsTransient() || this.IsTransient())
                    return false;
                else
                    return item.Id.Equals(this.Id);
            }
    
    
            public override int GetHashCode()
            {
                if (!IsTransient())
                {
                    if (!_requestedHashCode.HasValue)
                        _requestedHashCode = this.Id.GetHashCode() ^ 31;
    
                    return _requestedHashCode.Value;
                }
                else
                    return base.GetHashCode();
            }
    
    
    
            //表示对象是否为全新创建的,未持久化的
            public bool IsTransient()
            {
                return EqualityComparer<TKey>.Default.Equals(Id, default);
            }
    
            public override string ToString()
            {
                return $"[Entity: {GetType().Name}] Id = {Id}";
            }
    
    
            public static bool operator ==(Entity<TKey> left, Entity<TKey> right)
            {
                if (Object.Equals(left, null))
                    return (Object.Equals(right, null)) ? true : false;
                else
                    return left.Equals(right);
            }
    
            public static bool operator !=(Entity<TKey> left, Entity<TKey> right)
            {
                return !(left == right);
            }
        }
    领域对象实现

    4、领域事件接口

        public interface IDomainEvent : INotification
        {
        }

    在领域模型中领域事件是一个属性

            #region 
            private List<IDomainEvent> _domainEvents;
            public IReadOnlyCollection<IDomainEvent> DomainEvents => _domainEvents?.AsReadOnly();
    
            public void AddDomainEvent(IDomainEvent eventItem)
            {
                _domainEvents = _domainEvents ?? new List<IDomainEvent>();
                _domainEvents.Add(eventItem);
            }
    
            public void RemoveDomainEvent(IDomainEvent eventItem)
            {
                _domainEvents?.Remove(eventItem);
            }
    
            public void ClearDomainEvents()
            {
                _domainEvents?.Clear();
            }
            #endregion
    领域模型中的领域事件

    领域事件之应该在领域模型中调用处理,因为领域事件应该跟着领域业务变动触发。

    5、领域事件处理接口,借助MediatR

        public interface IDomainEventHandler<TDomainEvent> : INotificationHandler<TDomainEvent> 
            where TDomainEvent : IDomainEvent
        {
            //这里我们使用了INotificationHandler的Handle方法来作为处理方法的定义
            //Task Handle(TDomainEvent domainEvent, CancellationToken cancellationToken);
        }

    一般我们可以在领事件处理中向写队列通知,或者向EventBus写消息等等。

    6、值对象

        public abstract class ValueObject
        {
            protected static bool EqualOperator(ValueObject left, ValueObject right)
            {
                if (ReferenceEquals(left, null) ^ ReferenceEquals(right, null))
                {
                    return false;
                }
                return ReferenceEquals(left, null) || left.Equals(right);
            }
    
            protected static bool NotEqualOperator(ValueObject left, ValueObject right)
            {
                return !(EqualOperator(left, right));
            }
    
            protected abstract IEnumerable<object> GetAtomicValues();
    
            public override bool Equals(object obj)
            {
                if (obj == null || obj.GetType() != GetType())
                {
                    return false;
                }
                ValueObject other = (ValueObject)obj;
                IEnumerator<object> thisValues = GetAtomicValues().GetEnumerator();
                IEnumerator<object> otherValues = other.GetAtomicValues().GetEnumerator();
                while (thisValues.MoveNext() && otherValues.MoveNext())
                {
                    if (ReferenceEquals(thisValues.Current, null) ^ ReferenceEquals(otherValues.Current, null))
                    {
                        return false;
                    }
                    if (thisValues.Current != null && !thisValues.Current.Equals(otherValues.Current))
                    {
                        return false;
                    }
                }
                return !thisValues.MoveNext() && !otherValues.MoveNext();
            }
    
            public override int GetHashCode()
            {
                return GetAtomicValues()
                 .Select(x => x != null ? x.GetHashCode() : 0)
                 .Aggregate((x, y) => x ^ y);
            }
        }
    值类型

    protected abstract IEnumerable<object> GetAtomicValues();  获取原子值的方法。

    二、领域模型实现层

        public class Order : Entity<long>, IAggregateRoot
        {
            /// <summary>
            /// private set作用 领域模型的属性只能自己操作。符合开放封闭原则
            /// </summary>
            public string UserId { get; private set; }
    
            public string UserName { get; private set; }
    
            public Address Address { get; private set; }
    
            public int ItemCount { get; private set; }
    
            protected Order()
            { }
    
            public Order(string userId, string userName, int itemCount, Address address)
            {
                this.UserId = userId;
                this.UserName = userName;
                this.Address = address;
                this.ItemCount = itemCount;
    
                this.AddDomainEvent(new OrderCreatedDomainEvent(this));
            }
    
            /// <summary>
            /// 修改只能在领域模型内部操作
            /// </summary>
            /// <param name="address"></param>
            public void ChangeAddress(Address address)
            {
                this.Address = address;
                //this.AddDomainEvent(new OrderAddressChangedDomainEvent(this));
            }
        }
    Order模型
        public class Address : ValueObject
        {
            public string Street { get; private set; }
            public string City { get; private set; }
            public string ZipCode { get; private set; }
    
            public Address() { }
            public Address(string street, string city, string zipcode)
            {
                Street = street;
                City = city;
                ZipCode = zipcode;
            }
    
            protected override IEnumerable<object> GetAtomicValues()
            {
                // Using a yield return statement to return each element one at a time
                yield return Street;
                yield return City;
                yield return ZipCode;
            }
        }
    Address模型

    Order模型

    1、属性

    /// <summary>
    /// private set作用 领域模型的属性只能自己操作。符合开放封闭原则
    /// </summary>
    public string UserId { get; private set; }

    2、构造函数,创建对象

            public Order(string userId, string userName, int itemCount, Address address)
            {
                this.UserId = userId;
                this.UserName = userName;
                this.Address = address;
                this.ItemCount = itemCount;
    
                this.AddDomainEvent(new OrderCreatedDomainEvent(this));
            }
    View Code

    3、属性修改

    属性的修改,只能在模型内部,用具有意义的方法修改

            /// <summary>
            /// 修改只能在领域模型内部操作
            /// </summary>
            /// <param name="address"></param>
            public void ChangeAddress(Address address)
            {
                this.Address = address;
                //this.AddDomainEvent(new OrderAddressChangedDomainEvent(this));
            }
    View Code

    4、聚合根

    public class Order : Entity<long>, IAggregateRoot

    Address模型

    1、值类型

    public class Address : ValueObject

    必须实现GetAtomicValues获取元素方法

            protected override IEnumerable<object> GetAtomicValues()
            {
                // Using a yield return statement to return each element one at a time
                yield return Street;
                yield return City;
                yield return ZipCode;
            }
    View Code

    三、注意事项

    1、领域模型字段修改设为私有

    2、使用构造函数表示对象的创建

    3、使用具有业务含义的方法来操作模型字段

    4、领域模型负责对自己数据大的处理

    5、领域服务或者命令处理者(上层处理者)   负责调用领域模型业务动作(这样可以区分领域模型内在逻辑 和 外在逻辑)

    仓储设置

    一、工作单元

    使用同一个上线文

    实体状态跟踪

    保证事务一致性

    二、工作单元接口

        public interface IUnitOfWork : IDisposable
        {
            Task<int> SaveChangesAsync(CancellationToken cancellationToken = default);
            Task<bool> SaveEntitiesAsync(CancellationToken cancellationToken = default);
        }

    第一个返回影响条数

    第二个返回bool表示是否成功

    三、事务接口

        public interface ITransaction
        {
            IDbContextTransaction GetCurrentTransaction();
    
            bool HasActiveTransaction { get; }
    
            Task<IDbContextTransaction> BeginTransactionAsync();
    
            Task CommitTransactionAsync(IDbContextTransaction transaction);
    
            void RollbackTransaction();
        }

    GetCurrentTransaction

    获取当前事务

    HasActiveTransaction

    判断事物事物开始

    BeginTransactionAsync

    开始事务

    CommitTransactionAsync

    提交事务

    RollbackTransaction

    事务回滚

    四、EFContext实现事务

        public class EFContext : DbContext, IUnitOfWork, ITransaction
        {
            protected IMediator _mediator;
            ICapPublisher _capBus;
    
            public EFContext(DbContextOptions options, IMediator mediator, ICapPublisher capBus) : base(options)
            {
                _mediator = mediator;
                _capBus = capBus;
            }
    
            #region IUnitOfWork
            public async Task<bool> SaveEntitiesAsync(CancellationToken cancellationToken = default)
            {
                var result = await base.SaveChangesAsync(cancellationToken);
                await _mediator.DispatchDomainEventsAsync(this);
                return true;
            }
            #endregion
    
            #region ITransaction
    
            private IDbContextTransaction _currentTransaction;
            public IDbContextTransaction GetCurrentTransaction() => _currentTransaction;
            public bool HasActiveTransaction => _currentTransaction != null;
            public Task<IDbContextTransaction> BeginTransactionAsync()
            {
                if (_currentTransaction != null) return null;
                _currentTransaction = Database.BeginTransaction(_capBus, autoCommit: false);
                return Task.FromResult(_currentTransaction);
            }
    
            public async Task CommitTransactionAsync(IDbContextTransaction transaction)
            {
                if (transaction == null) throw new ArgumentNullException(nameof(transaction));
                if (transaction != _currentTransaction) throw new InvalidOperationException($"Transaction {transaction.TransactionId} is not current");
    
                try
                {
                    await SaveChangesAsync();
                    transaction.Commit();
                }
                catch
                {
                    RollbackTransaction();
                    throw;
                }
                finally
                {
                    if (_currentTransaction != null)
                    {
                        _currentTransaction.Dispose();
                        _currentTransaction = null;
                    }
                }
            }
    
            public void RollbackTransaction()
            {
                try
                {
                    _currentTransaction?.Rollback();
                }
                finally
                {
                    if (_currentTransaction != null)
                    {
                        _currentTransaction.Dispose();
                        _currentTransaction = null;
                    }
                }
            }
    
    
            #endregion
        }
    EFContext

    继承DbContext, IUnitOfWork, ITransaction

        public class TransactionBehavior<TDbContext, TRequest, TResponse> : IPipelineBehavior<TRequest, TResponse> where TDbContext : EFContext
        {
            ILogger _logger;
            TDbContext _dbContext;
            public TransactionBehavior(TDbContext dbContext, ILogger logger)
            {
                _dbContext = dbContext ?? throw new ArgumentNullException(nameof(dbContext));
                _logger = logger ?? throw new ArgumentNullException(nameof(logger));
            }
    
    
            public async Task<TResponse> Handle(TRequest request, CancellationToken cancellationToken, RequestHandlerDelegate<TResponse> next)
            {
                var response = default(TResponse);
                var typeName = request.GetGenericTypeName();
    
                try
                {
                    if (_dbContext.HasActiveTransaction)
                    {
                        return await next();
                    }
    
                    var strategy = _dbContext.Database.CreateExecutionStrategy();
    
                    await strategy.ExecuteAsync(async () =>
                    {
                        Guid transactionId;
                        using (var transaction = await _dbContext.BeginTransactionAsync())
                        using (_logger.BeginScope("TransactionContext:{TransactionId}", transaction.TransactionId))
                        {
                            _logger.LogInformation("----- 开始事务 {TransactionId} ({@Command})", transaction.TransactionId, typeName, request);
    
                            response = await next();
    
                            _logger.LogInformation("----- 提交事务 {TransactionId} {CommandName}", transaction.TransactionId, typeName);
    
    
                            await _dbContext.CommitTransactionAsync(transaction);
    
                            transactionId = transaction.TransactionId;
                        }
                    });
    
                    return response;
                }
                catch (Exception ex)
                {
                    _logger.LogError(ex, "处理事务出错 {CommandName} ({@Command})", typeName, request);
    
                    throw;
                }
            }
        }
    事务管理

    五、仓库实现

        public interface IRepository<TEntity> where TEntity : Entity, IAggregateRoot
        {
            IUnitOfWork UnitOfWork { get; }
            TEntity Add(TEntity entity);
            Task<TEntity> AddAsync(TEntity entity, CancellationToken cancellationToken = default);
            TEntity Update(TEntity entity);
            Task<TEntity> UpdateAsync(TEntity entity, CancellationToken cancellationToken = default);
            bool Remove(Entity entity);
            Task<bool> RemoveAsync(Entity entity);
        }
    
    
        public interface IRepository<TEntity, TKey> : IRepository<TEntity> where TEntity : Entity<TKey>, IAggregateRoot
        {
            bool Delete(TKey id);
            Task<bool> DeleteAsync(TKey id, CancellationToken cancellationToken = default);
            TEntity Get(TKey id);
            Task<TEntity> GetAsync(TKey id, CancellationToken cancellationToken = default);
        }
    仓库接口
        public abstract class Repository<TEntity, TDbContext> : IRepository<TEntity> where TEntity : Entity, IAggregateRoot where TDbContext : EFContext
        {
            protected virtual TDbContext DbContext { get; set; }
    
            public Repository(TDbContext context)
            {
                this.DbContext = context;
            }
            public virtual IUnitOfWork UnitOfWork => DbContext;
    
            public virtual TEntity Add(TEntity entity)
            {
                return DbContext.Add(entity).Entity;
            }
    
            public virtual Task<TEntity> AddAsync(TEntity entity, CancellationToken cancellationToken = default)
            {
                return Task.FromResult(Add(entity));
            }
    
            public virtual TEntity Update(TEntity entity)
            {
                return DbContext.Update(entity).Entity;
            }
    
            public virtual Task<TEntity> UpdateAsync(TEntity entity, CancellationToken cancellationToken = default)
            {
                return Task.FromResult(Update(entity));
            }
    
            public virtual bool Remove(Entity entity)
            {
                DbContext.Remove(entity);
                return true;
            }
    
            public virtual Task<bool> RemoveAsync(Entity entity)
            {
                return Task.FromResult(Remove(entity));
            }
        }
    
    
        public abstract class Repository<TEntity, TKey, TDbContext> : Repository<TEntity, TDbContext>, IRepository<TEntity, TKey> where TEntity : Entity<TKey>, IAggregateRoot where TDbContext : EFContext
        {
            public Repository(TDbContext context) : base(context)
            {
            }
    
            public virtual bool Delete(TKey id)
            {
                var entity = DbContext.Find<TEntity>(id);
                if (entity == null)
                {
                    return false;
                }
                DbContext.Remove(entity);
                return true;
            }
    
            public virtual async Task<bool> DeleteAsync(TKey id, CancellationToken cancellationToken = default)
            {
                var entity = await DbContext.FindAsync<TEntity>(id, cancellationToken);
                if (entity == null)
                {
                    return false;
                }
                DbContext.Remove(entity);
                return true;
            }
    
            public virtual TEntity Get(TKey id)
            {
                return DbContext.Find<TEntity>(id);
            }
    
            public virtual async Task<TEntity> GetAsync(TKey id, CancellationToken cancellationToken = default)
            {
                return await DbContext.FindAsync<TEntity>(id, cancellationToken);
            }
        }
    仓储实现

    MediatR和CAP和NserviceBus的知识自行学习

    MediatR学习

    CAP学习

    NServiceBus学习

    接口设计

    1、接口控制器作用

    定义输入输出

    身份认证

    授权

    不因该处理我们的领域模型,仓储

    2、分离

            IMediator _mediator;
            public OrderController(IMediator mediator)
            {
                _mediator = mediator;
            }
    
            [HttpPost]
            public async Task<long> CreateOrder([FromBody]CreateOrderCommand cmd)
            {
                return await _mediator.Send(cmd, HttpContext.RequestAborted);
            }
    
    
    
            [HttpGet]
            public async Task<List<string>> QueryOrder([FromQuery]MyOrderQuery myOrderQuery)
            {
                return await _mediator.Send(myOrderQuery);
            }

    用mediatr分离控制器和一些业务逻辑

    尽量使用一部

    集成事件 EventBus

    集成事件工作原理: 

    目的实现我们系统的集成,系统里面多个微服务之间相互传递事件

    集成事件的实现:

    1、如上图发布订阅模式

    在mediatr的处理者中,发布消息,其他服务订阅,(消息一般发布队列中)。 现成的发布订阅组件  cap   nervicebus等等,也可以自己封装(写对列 消费)

    2、观察者模式。(由观察者将事件发给关注的人)

    RabbitMQ实现集成事件的发布订阅

    cap+rabbitmq

    nservicebus+rabbitmq

    自己封装事件组件+rabbitmq

    总结:

    集成事件是跨服务的领域事件

    集成事件一般由领域事件驱动触发。(业务触发领域事件   领域事件  发送集成事件)

    不通过事务来处理集成事件(实现最终一致性)

    仅在必要的情况下定义和使用集成事件

  • 相关阅读:
    移动端疫情展示
    第五周学习进度
    第四周学习进度
    结队开发-四则运算
    第三周学习进度
    全球疫情可视化第一阶段
    第二周学习进度
    面试题 02.07. 链表相交 做题小结
    剑指 Offer 35. 复杂链表的复制 做题小结
    LeetCode 452. 用最少数量的箭引爆气球 做题小结
  • 原文地址:https://www.cnblogs.com/wudequn/p/13058173.html
Copyright © 2011-2022 走看看