پیاده سازی پروژه API در دات نت ، قسمت دوم ، ApplicationServices و DataLayer

سلام :) در ادامه ی قسمت قبلپیاده سازی پروژه API در دات نت ، قسمت اول ، معماری پیاز (Onion Architecture) کارمون رو با توسعه ی قسمت ApplicationServices ادامه میدیم .در ابتدا باید بدونیم این لایه قراره چه کاری انجام بده لایه اپلیکیشن در بر گیرنده عملیاتی است که قوانین بیزینس ایجاب میکند که روی انتیتی های سیستم صورت بگیره. به عنوان مثال عملیات ذخیره اطلاعات مشتری ، خوندن اطلاعات و یا هر عملیاتی که در بیزینس ما تعریف شده .در نظر داشته باشید که ذخیره اطلاعات عملیاتی است که با مکانیزم های ذخیره و بازیابی اطلاعات مرتبط است و تو لایه ی DataLayer در قسمت 02.Infrastructures پیاده سازی میشن. بنابر این دیتابیس و جزییات نحوه ذخیره اطلاعات باید از قوانین بیزینس سیستم جدا باشند و این کار توسط پروژه ی DataLayer.SqlServer داخل پوشه ی 02.Infrastructures انجام میشه .میریم برای پیاده سازی قسمت ApplicationServices کارمون رو رو شروع میکنیم ، ابتدا داخل پروژه ی ApplicationServices که داخل پوشه ی Src و 01.Core هستش ، دو پوشه تعریف میکنیم ، پوشه های Models و Services و داخل اونها بر اساس انتیتتی هایی که داریم ( بر اساس مقاله قبلی در این مثال دو موجودیت User و UserExam وجود دارن ) پوشه های جدا میسازیم در نهایت باید پوشه بندی ما مثل شکل زیر باشه.در پوشه Model ، مدل های کاری ما قرار میگیره ، مثلا DTO ها برای انتقال دیتا و یا DtoValidation ها برای اعتبار سنجی مدل های انتقال دیتا مون ، فعلا با پوشه ی Model کاری نداریم. میرسیم به پوشه ی Services که پیاده سازی عملیات های مورد نیاز ماست ، در این قسمت برای هر موجودیت یک کلاس اینترفیس و یک کلاس برای خود سرویس تعریف میکنیم ، مانند شکل زیرخب ، میریم برای نوشتن سرویس هامون ، اما در حال حاظر نمیدونیم که قرار چه عملیات هایی رو تو ای سرویس ها میخوایم پیاده کنیم ، باید جلوتر بریم تا عملیات هامون رو بدونیم و تو سرویس خودش پیاده کنیم ، مثلا برای ثبت نام کاربر ما نیاز به متد Register داخل سرویس UserService داریم .قبل از پیاده سازی متد های سرویس هامون نیاز به پیاده سازی یک ریپازیتوری برای ذخیره و بازیابی اطلاعاتمون داریم ، بنابرین میریم که ریپازیتوری هامون رو داخل لایه ی 02.Infrastructures داخل پروژه ی DataLayer.SqlServer پیاده سازی کنیم .از اینجا شروع به پیاده سازی لایه DataLayer میکنیم.در نظر داشته باشید که قالب یا همون اینترفیس ریپازیتوری هامون ( IRepository ) رو قبلتر داخل Core و پروژه ی DomainClass مون نوشتیم و باید از اون فایل ارث بری کرده و پیاده سازی کنیم.از اونجا که قصد داریم برای دیتا بیسمون از SqlServer و ORM قدرتمند EF استفاده کنیم میریم برای اینکه پکیج های مورد نیازمون رو از نوگت نصب کنیم.دو پکیج زیر رو به پروژه ی DataLayer.SqlServer که داخل پوشه ی 02.Infrastructures هست رو اضافه میکنیم.Microsoft.EntityFrameworkCore.SqlServerMicrosoft.EntityFrameworkCore.Toolsپکیج Microsoft.EntityFrameworkCore.SqlServer برای کار با SqlServer و Ef هستپکیج Microsoft.EntityFrameworkCore.Tools یکسری ابزار برای انجام کارمون مثل دستورات add migration و update database در اختیارمون میزاره.بعد از نصب پکیج ها میریم تا پوشه بندی لایه ی DataLayer در داخل پروژه ی DataLayer.SqlServer انجام بدیم.یک پوشه Common تعریف میکنیم برای کلاس های عمومی تر مثل ApplicationContext که پل ارتباطی با دیتا بیس هستش و کلاس EFRepository که قراره از IRepository که داخل لایه ی DomainClass مون هستش ارث بری کنه و متد های مورد نیازمون رو پیاده سازی کنه.یک پوشه به نام EfConfiguration که برای کلاس های تنظیمات دیتا بیسی ماست ، به ازای هر انتیتی تنظیماتی که میخواهیم در دیتا بیس انجام بدیم رو توسط کلاس ها داخل این پوشه تعریف میکنیم پس باید برای هر موجودیت هامون که داخل دیتا بیس جدول دارن یک کلاس برای تنظیمات بسازیم.منظور از تنظیماتی مثل نوع داده ، طول فیلد و ... هستشدر آخر هم پوشه به نام Repositories میزاریم که قرار هستش برای هر انتیتی عملیات داخل اینترفیسی که داخل DomainClass برای هر موجودیت رو تعریف کردیم توسط EfRepository پیاده کنه.مثلا برای ازمون های کاربران اینترفیس IUserRepository رو داخل Domain تعریف کردیم که قراره توسط کلاس UserRepository ارث بری بشه و عملیات های داخل اون پیاده سازی بشه.در نهایت پوشه ها و کلاس های ما داخل پروژه DataLayer.SqlServer شکل زیر رو تشکیل میده.در ادامه کلاس ApplicationContext رو کامل میکنیم .ApplicationContext.cspublic class ApplicationContext : DbContext { public DbSet<UserExam> UserExams { get; set; } public DbSet<User> Users { get; set; } public ApplicationContext(DbContextOptions options) : base(options) { } protected override void OnModelCreating(ModelBuilder modelBuilder) { modelBuilder.ApplyConfigurationsFromAssembly(typeof(ApplicationContext).Assembly); modelBuilder.Entity<UserExam>().HasQueryFilter(c => c.IsDeleted == false); base.OnModelCreating(modelBuilder); } }در این کلاس از DbContext ارث بری کردیم ، در EF برای اینکه پل ارتباطی یا همون کانتکست به دیتا بیس رو مشخص کنیم باید کلاسی داشته باشیم که از DbContext ارث بری کرده باشه. برای مشخص کردن جداول مون داخل دیتا بیس هر موجودیت رو به این شکل تعریف میکنیمpublic DbSet<UserExam> UserExams { get; set; } public DbSet<User> Users { get; set; }در نظر داشته باشید که کلاس های UserExam و User که موجودیت های ما هستند داخل لایه ی Domain تعریف شدند.در ادامه یک متد سازنده برای این کلاس داریم تا بتونیم تنظیماتی مثل کانکشن استرینگ به دیتا بیسمون رو تعریف کنیم public ApplicationContext(DbContextOptions options) : base(options) { }و در آخر هم متد OnModelCreating رو بازنویسی یا override میکنیم تا یکسری کارها رو انجام بدیم.protected override void OnModelCreating(ModelBuilder modelBuilder) { modelBuilder.ApplyConfigurationsFromAssembly(typeof(ApplicationContext).Assembly); modelBuilder.Entity<UserExam>().HasQueryFilter(c => c.IsDeleted == false); base.OnModelCreating(modelBuilder); }در این متد گفتیم که تنظیماتی که میخواهیم برای موجودیت ها یا همون جدول های دیتا بیس لحاظ کردیم رو از فضای نام همین قسمت بخونmodelBuilder.ApplyConfigurationsFromAssembly(typeof(ApplicationContext).Assembly);در ادامه تعریف کردیم که موجودیت آزمون ما در صورتی که فیلد IsDeleted آن False بود درنظر بگیر ، این مورد برای همان SoftDelete هست که در قسمت اول گفتیمmodelBuilder.Entity<UserExam>().HasQueryFilter(c => c.IsDeleted == false);در آخر هم متد کلاس پایه رو فراخونی کردیم تا کار ادامه پیدا کنه base.OnModelCreating(modelBuilder);خب میریم سراغ پوشه EfConfiguration برای تنظیمات دیتا بیسقبلتر هم گفتیم که به ازای هر موجودیت که قراره جدولی داخل دیتا بیسمون بشه کلاس میسازیم و تنظیماتمون رو داخلش اعلام میکنیم.مثل کلاس زیر برای تنظیمات کاربر یا موجودیت UserUserConfiguration.cspublic class UserConfiguration : IEntityTypeConfiguration<User> { public void Configure(EntityTypeBuilder<User> builder) { builder.Property(cb => cb.Mobile) .IsRequired() .HasMaxLength(11); builder.Property(cb => cb.Name) .IsRequired() .HasMaxLength(100); builder.Property(cb => cb.Password) .IsRequired() .HasMaxLength(100); } }این حالت که برای تنظیمات دیتا بیس کلاس جدا تعریف میکنیم در واقع Fluent Api میگن که خودش مبحث بزرگیه ، در کل روش های دیگه ای برای تعریف تنظیمات دیتا بیسی داخل EF وجود داره ولی تمیز ترین حالت همین کاره.در اینجا به عنوان مثال توسط IsRequired و HasMaxLength ، اجباری بودن و حداکثر طول فیلد رو مشخص کردیم.برای موجودیت آزمون کاربران هم همین کار رو انجام میدیم.خب الان که کلاس ApplicationContext که همون پل ارتباطمون با دیتا بیس هستش و همچنین تنظیماتی که روی جداول دیتا بیسمون لازم داریم رو ساختیم میتونیم کلاس EFRepository که قراره اینترفیس IRepository که داخل DomainClass مون تعریف شده رو پیاده کنه بسازیم.کلاس EFRepository به این شکل میشهEFRepository.cspublic class EfRepository<TEntity> : IRepository<TEntity> where TEntity : BaseEntity, new() { protected readonly ApplicationContext dbContext; public EfRepository(ApplicationContext dbContext) { this.dbContext = dbContext; } public void Insert(TEntity entity) { dbContext.Add(entity); dbContext.SaveChanges(); } public void Remove(TEntity entity) { dbContext.Remove(entity); dbContext.SaveChanges(); } public TEntity Get(int id) { return dbContext.Set<TEntity>().FirstOrDefault(c => c.Id == id); } public IEnumerable<TEntity> GetAll() { return dbContext.Set<TEntity>().ToList(); } public void Update(TEntity entity) { dbContext.Update(entity); dbContext.SaveChanges(); } public void SaveChanges() { dbContext.SaveChanges(); } public IQueryable<TEntity> GetQueryable() { return dbContext.Set<TEntity>().AsQueryable(); } }کلاس EFRepository که از کلاس IRepository ارث بری کرده و شرط ارث بری از BaseEntity که داخل لایه Domain مون هستش رو هم داره.پیاده سازی متد ها یی که داخل اینترفیس IRepository هستش رو توسط EF با کلاس ApplicationContext که پل ارتباطیمون با دیتا بیس هست و داخل سازنده ی کلاس تزریق شده رو بصورت جنریک میبینیم.حالا میتونیم برای هر موجودیتی که داریم ریپازیتوری تعریف کنیم ، به این شکل که کلاس های UserExamRepository و UserRepository رو داخل پوشه ی Repositories در لایه ی DataLayer و پروژه ی DataLayer.SqlServer میسازیم.در حال حاظر متد هایی که در EfRepository تعریف و پیاده شدند برای این دو کلاس کافی هستش و از کلاس EfRepository هم ارث بری میکنیم و به این شکل کلاس UserRepository و UserExamRepository رو میسازیمUserExamRepository.cs public class UserExamRepository : EfRepository<UserExam>, IUserExamRepository { public UserExamRepository(ApplicationContext DbContext) : base(DbContext) { } }UserRepository.cspublic class UserRepository : EfRepository<User>, IUserRepository { public UserRepository(ApplicationContext DbContext) : base(DbContext) { } } ممکن هستش در آینده متد های بیشتری بر اساس نیاز سرویس هامون بخوایم اضافه کنیم به ریپازیتوری ها که در آینده این کار رو انجام میدیم.تا اینجا ریپازیتوری هامون رو هم اضافه کردیم و لایه ی DataLayer کامل میشه.در ادامه نیاز داریم سرویس های مورد نیازمون که در لایه نهایی یا همون EndPoint لازم داریم رو بنویسیم ، به عنوان مثال میخوایم کاربر جدید ثبت کنیم بنابرین سرویس یوزر داخل کلاس UserService باید پیاده سازی ثبت نام رو داشته باشه ( یوزر جدید داخل دیتا بیس ثبت بشه ) تا داخل EndPoint هامون ازش استفاده کنیم.ادامه کار رو در مقاله بعدی با پیاده سازی EndPoint که یک RestApi هستش در ادامه خواهیم دید.البته هنوز کلی از کارمون مونده ولی سورس کامل این مقالات رو میتونید از اینجا ببینید.امیدوارم از این مطلب خوشتون اومده باشه :)پیاده سازی پروژه API در دات نت ، قسمت اول ، معماری پیاز (Onion Architecture)پیاده سازی پروژه API در دات نت ، قسمت دوم ، ApplicationServices و DataLayerپیاده سازی پروژه API در دات نت ، قسمت سوم ، EndPoint توسط RestAPIپیاده سازی پروژه API در دات نت ، قسمت چهارم ، RestAPI Authentication Jwtپیاده سازی پروژه API در دات نت ، قسمت پنجم ، عملیات CRUD در RestAPIپیاده سازی پروژه API در دات نت ، قسمت ششم ( قسمت آخر ) ، عملیات CRUD در RestAPI پارت دوم