آموزش کاربردی DDD in ASP.NET Core - قسمت دوم

DDD in ASP.NET Core applicationsادامه از قسمت اول : آموزش کاربردی DDD in ASP.NET Core - قسمت اولپیاده سازی DDD در یک .NET Core application.اجازه دهید یک مثال اساسی را توضیح دهم که به شما کمک می کند این معماری را به وضوح درک کنید و بدانید که چگونه آن را به طور مؤثر پیاده سازی کنید.همانطور که قبلا و در قسمت اول از این مبحث ذکر شد، یک سیستم با پیاده سازی الگوی DDD معمولاً از سه لایه اصلی تشکیل شده است: Application، Domain و Infrastructure و می تواند در solution ی مانند زیر سازماندهی شود.لایه API Layerاین لایه Application است و به عنوان دروازه ای عمل می کند که در آن برنامه ها (Presentation Layer) با سیستم تعامل دارند. این لایه اطلاعات جمع آوری شده از تعاملات بین برنامه و کاربران نهایی یا خدمات شخص ثالث(third-party services) را پردازش می کند. درخواست‌ها را دریافت می‌کند و قبل از ارسال آن‌ها به دامنه(Domain) برای پردازش، ورودی را تأیید(validates) می‌کند. API همچنین پاسخ هایی(responses) را به کلاینت ارائه می دهد.تصویر زیر ساختار دقیق لایه API را توضیح می دهد.BaseService.cspublic class BaseService { public BaseService(IUnitOfWork unitOfWork) { UnitOfWork = unitOfWork; } protected internal IUnitOfWork UnitOfWork { get; set; } }UserService.cspublic class UserService : BaseService { public UserService(IUnitOfWork unitOfWork) : base(unitOfWork) { } public async Task AddNewAsync(AddUserRequest model) { // You can you some mapping tools as such as AutoMapper var user = new User(model.UserName , model.FirstName , model.LastName , model.Address , model.BirthDate , model.DepartmentId.Value); var repository = UnitOfWork.AsyncRepository(); await repository.AddAsync(user); await UnitOfWork.SaveChangesAsync(); var response = new AddUserResponse() { Id = user.Id, UserName = user.UserName }; return response; } public async Task AddUserPayslipAsync(AddPayslipRequest model) { var repository = UnitOfWork.AsyncRepository(); var user = await repository.GetAsync(_ => _.Id == model.UserId); if (user != null) { var payslip = user.AddPayslip(model.Date.Value , model.WorkingDays.Value , model.Bonus , model.IsPaid.Value); await repository.UpdateAsync(user); await UnitOfWork.SaveChangesAsync(); return new AddPayslipResponse() { UserId = user.Id, TotalSalary = payslip.TotalSalary }; } throw new Exception(&quotUser not found.&quot); } public async Task<List> SearchAsync(GetUserRequest request) { var repository = UnitOfWork.AsyncRepository(); var users = await repository .ListAsync(_ => _.UserName.Contains(request.Search)); var userDTOs = users.Select(_ => new UserInfoDTO() { Address = _.Address, BirthDate = _.BirthDate, DepartmentId = _.DepartmentId, FirstName = _.FirstName, Id = _.Id, LastName = _.LastName, UserName = _.UserName }) .ToList(); return userDTOs; } }لایه Domain Layerاینجا مرکز سیستم است. دامنه بیشتر منطق کسب و کار سیستم را کنترل می کند. این لایه همچنین وظیفه تعریف مفاهیم، ​​رفتارها و قوانین را بر عهده دارد. لایه های باقی مانده آنها را پیاده سازی می کنند.بیشتر بخوانید : لایه ها در DDD - دامنه (Domain)a. The EntitiesUser.cspublic partial class User : BaseEntity { public User() { PaySlips = new HashSet(); } public string UserName { get; private set; } public string FirstName { get; private set; } public string LastName { get; private set; } public string Address { get; private set; } public DateTime? BirthDate { get; private set; } public int DepartmentId { get; private set; } public float CoefficientsSalary { get; private set; } public virtual Department Department { get; private set; } public virtual ICollection PaySlips { get; private set; } }User.Aggregate.cspublic partial class User: IAggregateRoot { public User(string userName , string firstName , string lastName , string address , DateTime? birthDate , int departmentId) { UserName = userName; this.Update( firstName , lastName , address , birthDate , departmentId ); } public void Update(string firstName , string lastName , string address , DateTime? birthDate , int departmentId) { FirstName = firstName; LastName = lastName; Address = address; BirthDate = birthDate; DepartmentId = departmentId; } public void AddDepartment(int departmentId) { DepartmentId = departmentId; } public Payslip AddPayslip(DateTime date , float workingDays , decimal bonus , bool isPaid ) { // Make sure there's only one payslip per month var exist = PaySlips.Any(_ => _.Date.Month == date.Month && _.Date.Year == date.Year); if (exist) throw new Exception(&quotPayslip for this month already exist.&quot); var payslip = new Payslip(this.Id, date, workingDays, bonus); if (isPaid) { payslip.Pay(this.CoefficientsSalary); } PaySlips.Add(payslip); var addEvent = new OnPayslipAddedDomainEvent() { Payslip = payslip }; AddEvent(addEvent); return payslip; } } Payslip.cspublic class Payslip : BaseEntity { public Payslip(int userId , DateTime date , float workingDays , decimal bonus) { UserId = userId; Date = date; WorkingDays = workingDays; Bonus = bonus; } public PayslipValueObject Value; public DateTime Date { get; private set; } public float WorkingDays { get; private set; } public bool IsPaid { get; private set; } public DateTime? PaymentDate { get; private set; } public int UserId { get; private set; } public decimal TotalSalary { get; private set; } public decimal Bonus { get; private set; } public virtual User User { get; private set; } public void Pay( float coefficientsSalary ) { if (IsPaid) throw new Exception(&quotThis Payslip has been paid.&quot); IsPaid = true; Value = new PayslipValueObject(WorkingDays, coefficientsSalary, Bonus); TotalSalary = Value.TotalSalary; PaymentDate = DateTime.Now; } } Department.cspublic partial class Department : BaseEntity { public string Name { get; internal set; } public string Description { get; internal set; } public virtual ICollection Users { get; internal set; } } Department.Aggregate.cspublic partial class Department: IAggregateRoot { public Department() { Users = new HashSet(); } public Department(string name , string description) : this() { this.Update(name, description); } public void Update(string name , string description) { Name = name; Description = description; } }b. The Generic repositoriesIAsyncRepository.cspublic interface IAsyncRepository where T : BaseEntity { Task AddAsync(T entity); Task UpdateAsync(T entity); Task DeleteAsync(T entity); Task GetAsync(Expression<Func<T, bool>> expression); Task<List> ListAsync(Expression<Func<T, bool>> expression); }IUnitOfWork.cspublic interface IUnitOfWork { { Task SaveChangesAsync(); IAsyncRepository Repository() where T : BaseEntity; } }لایه Infrastructure Layerاین لایه سیستم شامل database, logging, and exceptions , ... است. این لایه محل تعامل با پایگاه داده است. از طریق رفتارها و قوانین، کلاس های POCO در Domain تعریف شده است. این لایه کلیه عملیات مربوط به ذخیره سازی اطلاعات سیستم را بر عهده می گیرد.UnitOfWork.cspublic class UnitOfWork : IUnitOfWork { private readonly EFContext _dbContext; public UnitOfWork(EFContext dbContext) { _dbContext = dbContext; } public IAsyncRepository AsyncRepository() where T : BaseEntity { return new RepositoryBase(_dbContext); } public Task SaveChangesAsync() { return _dbContext.SaveChangesAsync(); } }RepositoryBase.cspublic class RepositoryBase : IAsyncRepository where T : BaseEntity { private readonly DbSet _dbSet; public RepositoryBase(EFContext dbContext) { _dbSet = dbContext.Set(); } public async Task AddAsync(T entity) { await _dbSet.AddAsync(entity); return entity; } public Task DeleteAsync(T entity) { _dbSet.Remove(entity); return Task.FromResult(true); } public Task GetAsync(Expression<Func<T, bool>> expression) { return _dbSet.FirstOrDefaultAsync(expression); } public Task<List> ListAsync(Expression<Func<T, bool>> expression) { return _dbSet.Where(expression).ToListAsync(); } public Task UpdateAsync(T entity) { _dbSet.Update(entity); return Task.FromResult(entity); } }UserRepository.cspublic class UserRepository : RepositoryBase , IUserRepository { public UserRepository(EFContext dbContext) : base(dbContext) { } }DepartmentRepository.cspublic class DepartmentRepository : RepositoryBase , IDepartmentRepository { public DepartmentRepository(EFContext dbContext) : base(dbContext) { } }مشاهده سورس کامل پروژهنتیجهبه طور خلاصه، DDD یک الگوی عالی برای سیستم هایی با منطق کسب و کار پیچیده است، سیستم هایی که نیاز به نگهداری و توسعه در آینده دارند. لزومی ندارد که این الگو را به طور کامل در پروژه اعمال کنیم، گاهی اوقات بسته به موقعیت های خاص در هر پروژه باید معاوضه هایی را انجام دهیم. Onion Architecture Clean Architecture و Hexagon Architecture نیز نمونه های خوبی هستند.بیشتر بخوانید : نقشه راه توسعه دهندگان Asp.NET Corehttps://zarinp.al/farshidazizi