基於ABP VNext及CodeFirst,邁開走向DDD的第一步


   前言: DDD的基礎知識這里就不講了,網上有很多,無外乎 架構從以外的三層變成四層,有聚合、實體、值對象、領域服務等這些概念,我也是最近看了很多,但無從下手,正好我們現有的項目是基於ABP

框架的,雖說也支持DDD,也是分為了4個項目,有領域有Domain,但感覺還是個三層項目,想了想,最大的問題還是收到新的任務后,總是從數據庫開始,然后T4生成實體,沒有太多的去進行領域划分。所以本次

我們使用EFCore的CodeFirst和ABPVnext來體驗一下怎么在項目中真正的運用DDD。

--------------------------------------------------------------------------------------------------------------------------------------------------

  新建項目

  

 

 

  我這里使用ABP的CLI命令創建的,當然也可以直接在網站(https://www.abp.io/get-started)上下載

 

  我這里由於沒有裝mssql,所以切換到了mysql,具體可參考https://docs.abp.io/zh-Hans/abp/latest/Entity-Framework-Core-MySQL

 

切換后,我們使用下面這倆命令創建一下數據庫

Add-Migration "Init"
Update-DataBase

 

~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

重點來了

  接下來我們正式從一個訂單的領域開始, 首先看一張圖

 

 

 

首先我們先創建個地址的值對象 

  /// <summary>
    /// 地址(值對象)
    /// </summary>
    public class Address : ValueObject
    {
        public String Street { get; private set; }
        public String City { get; private set; }
        public String State { get; private set; }
        public String Country { get; private set; }
        public String ZipCode { get; private set; }

        public Address() { }

        public Address(string street, string city, string state, string country, string zipcode)
        {
            Street = street;
            City = city;
            State = state;
            Country = country;
            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 State;
            yield return Country;
            yield return ZipCode;
        }
    }

 

創建實體

 /// <summary>
    /// 訂單明細實體
    /// </summary>
    public class OrderItem : Entity
    {
        public virtual Guid OrderId { get; protected set; }

        public virtual Guid ProductId { get; protected set; }

        public virtual int Count { get; protected set; }

        protected OrderItem()
        {

        }

        internal OrderItem(Guid orderId, Guid productId, int count)
        {
            OrderId = orderId;
            ProductId = productId;
            Count = count;
        }

        internal void ChangeCount(int newCount)
        {
            Count = newCount;
        }

        public override object[] GetKeys()
        {
            return new Object[] { OrderId, ProductId };
        }
    }

 

創建訂單聚合根

 /// <summary>
    /// 訂單聚合根
    /// </summary>
    public class Order : AggregateRoot<Guid>
    {
        public virtual string ReferenceNo { get; protected set; }

        public virtual int TotalItemCount { get; protected set; }

        public virtual DateTime CreationTime { get; protected set; }

        public virtual List<OrderItem> OrderLines { get; protected set; }

        public Address Address { get; protected set; }//值對象

        public string UserName { get; protected set; }

        protected Order()
        {

        }

        public Order(Guid id, string referenceNo, string userName, Address address)
        {
            Check.NotNull(referenceNo, nameof(referenceNo));

            Id = id;
            ReferenceNo = referenceNo;
            Address = address;
            OrderLines = new List<OrderItem>();
            CreationTime = DateTime.Now;
            UserName = userName;
        }

        public void AddProduct(Guid productId, int count)
        {
            if (count <= 0)
            {
                throw new ArgumentException(
                    "You can not add zero or negative count of products!",
                    nameof(count)
                );
            }

            var existingLine = OrderLines.FirstOrDefault(ol => ol.ProductId == productId);

            if (existingLine == null)
            {
                OrderLines.Add(new OrderItem(this.Id, productId, count));
            }
            else
            {
                existingLine.ChangeCount(existingLine.Count + count);
            }

            TotalItemCount += count;
        }
    }

 

然后在Context中添加

public DbSet<Order> Orders { get; set; }

public DbSet<OrderItem> OrderItems { get; set; }
public static void ConfigureDDDTest(this ModelBuilder builder)
        {
            Check.NotNull(builder, nameof(builder));

            /* Configure your own tables/entities inside here */

            //builder.Entity<YourEntity>(b =>
            //{
            //    b.ToTable(DDDTestConsts.DbTablePrefix + "YourEntities", DDDTestConsts.DbSchema);

            //    //...
            //});

            builder.Entity<Order>(b =>
            {
                b.ToTable(DDDTestConsts.DbTablePrefix + "Orders", DDDTestConsts.DbSchema);
                b.ConfigureByConvention();
                b.OwnsOne(o => o.Address, a => {
                    a.WithOwner();
                });
                //b.Property(x => x.Name).IsRequired().HasMaxLength(128);
            });

            builder.Entity<OrderItem>(b =>
            {
                b.ToTable(DDDTestConsts.DbTablePrefix + "OrderLines", DDDTestConsts.DbSchema);
                b.ConfigureByConvention();
                b.HasKey(x => new { x.OrderId, x.ProductId });
                //b.Property(x => x.Name).IsRequired().HasMaxLength(128);
            });
        }

此時我們再執行遷移命令,會發現數據庫中多了倆張表,值對象在聚合中

 

 

 

 

~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

這時候我們執行個創建的方法是,會出現這個錯誤

 

這個時候只需要把 base.OnModelCreating(builder);,移到builder.ConfigureDDDTest();下面即可

 

 

 

 

自定義倉儲的話,我們需要把接口定義在領域層,把實現放在基礎設施層

 

 

 public class OrderRepository : EfCoreRepository<DDDTestDbContext, Order, Guid>, IOrderRepository
    {
        public OrderRepository(IDbContextProvider<DDDTestDbContext> dbContextProvider)
     : base(dbContextProvider)
        {

        }

        public async Task<Order> Get(Guid orderId)
        {

            var order = await DbContext
                               .Orders
                               .Include(x => x.Address)
                               .FirstOrDefaultAsync(o => o.Id == orderId);
            if (order == null)
            {
                order = DbContext
                            .Orders
                            .Local
                            .FirstOrDefault(o => o.Id == orderId);
            }
            if (order != null)
            {
                await DbContext.Entry(order)
                    .Collection(i => i.OrderLines).LoadAsync();
            }

            return order;
        }
    }

 

 

 

 

 

 

 

 

 整體實現下來,不知道有沒有點對DDD的感覺呢?

參考:  

https://www.cnblogs.com/richieyang/p/5373250.html
https://cn.abp.io/blog/Abp/Abp-vNext-Announcement
https://docs.abp.io/en/abp/latest/
https://github.com/abpframework/abp/issues/2640
https://docs.microsoft.com/zh-cn/dotnet/architecture/microservices/microservice-ddd-cqrs-patterns/microservice-domain-model
https://github.com/dotnet-architecture/eShopOnContainers

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM