ABP Framework ile Adım Adım Yazılım Geliştirme – Bölüm 4: Domain Layer Geliştirme

Admin 

Domain Layer (Domain Katmanı), uygulamanızın çekirdek iş mantığını ve kurallarını barındıran en önemli katmanıdır. Bu katman, veritabanı veya UI gibi dış detaylardan bağımsızdır ve uygulamanın işleyişinin kalbidir. Bu bölümde, domain katmanınızı zenginleştirecek anahtar kavramları inceleyeceğiz.

Domain Service’ler Oluşturma

Domain Service (Domain Servisi), tek bir entity’ye ait olmayan, ancak birden fazla entity’yi veya aggregate root’u içeren iş mantığı için kullanılır. Genellikle bir fiili veya bir işlemi temsil eder.

Örnek: İki kitabı birleştiren veya bir kitabı başka bir kategoriye taşıyan bir domain servisi.

C#

using System;
using System.Threading.Tasks;
using Volo.Abp.Domain.Services;
using Volo.Abp.Domain.Repositories;

namespace MyProject.Books
{
    public class BookManager : DomainService
    {
        private readonly IRepository<Book, Guid> _bookRepository;

        public BookManager(IRepository<Book, Guid> bookRepository)
        {
            _bookRepository = bookRepository;
        }

        public async Task ChangeBookTypeAsync(Book book, BookType newType)
        {
            if (book.Type == newType)
            {
                return; // Tür zaten aynıysa işlem yapma
            }

            // İş kuralı: Belirli bir türden diğerine geçiş kısıtlaması olabilir
            if (book.Type == BookType.Horror && newType == BookType.Adventure)
            {
                throw new BusinessException(MyProjectDomainErrorCodes.HorrorToAdventureNotAllowed);
            }

            book.Type = newType;
            await _bookRepository.UpdateAsync(book);
        }

        // Başka domain işlemleri...
    }
}
  • DomainService sınıfından türeyerek servislerinizdeki bağımlılık enjeksiyonunu kolaylaştırırsınız.
  • Domain servisleri genellikle IRepository arayüzleri aracılığıyla entity’lere erişir.

Business Logic Implementasyonu

Domain katmanında iş mantığını implemente ederken, entity’lerin kendi iç tutarlılıklarını korumalarını sağlamak önemlidir. İş kuralları mümkün olduğunca entity’lerin kendi metotları içine yerleştirilmelidir. Eğer iş kuralı birden fazla entity’yi ilgilendiriyorsa, o zaman bir domain servisi kullanılabilir.

Örnek: Book entity’si içinde bir iş kuralı:

C#

public class Book : AggregateRoot<Guid>
{
    public string Name { get; private set; } // Private setter ile kontrollü değişim
    public BookType Type { get; set; }
    public DateTime PublishDate { get; set; }
    public float Price { get; set; }

    // Yapıcı metod
    private Book() { /* EF Core için boş yapıcı metod */ }

    public Book(Guid id, string name, BookType type, DateTime publishDate, float price)
        : base(id)
    {
        SetName(name); // İş kuralı uygulama
        Type = type;
        PublishDate = publishDate;
        Price = price;
    }

    public void SetName(string name)
    {
        if (string.IsNullOrWhiteSpace(name))
        {
            throw new ArgumentException("Book name cannot be empty.", nameof(name));
        }
        if (name.Length > 128)
        {
            throw new ArgumentException("Book name cannot exceed 128 characters.", nameof(name));
        }
        Name = name;
    }
}

Domain Event’ler

Domain Event (Domain Olayı), domain içindeki bir şeyin olduğunu ve diğer domain bileşenlerinin ilgilenebileceği bir olayı temsil eder. Domain olayları, farklı aggregate root’lar veya modüller arasında gevşek bağlı iletişimi sağlamak için kullanılır.

ABP Framework, Event Bus (Bkz. Bölüm 11) mekanizması ile domain olaylarını kolayca yayınlamanıza ve dinlemenize olanak tanır.

  1. Domain Event Tanımlama: Volo.Abp.EventBus.Distributed.IDistributedEvent veya Volo.Abp.EventBus.Local.ILocalEvent arayüzlerini implemente eden bir DTO sınıfı. C#public class BookCreatedEto // ETO: Event Transfer Object { public Guid BookId { get; set; } public string BookName { get; set; } // Diğer ilgili veriler }
  2. Olay Yayınlama: Bir domain servisi veya entity içerisinde IDomainEventBus servisi aracılığıyla olay yayınlanır. C#// BookManager içinde public async Task CreateBookAsync(string name, BookType type, ...) { // Kitap oluşturma mantığı... var book = new Book(...); await _bookRepository.InsertAsync(book); // Olayı yayınla await _eventBus.PublishAsync(new BookCreatedEto { BookId = book.Id, BookName = book.Name }); }
  3. Olay Dinleme (Handler): IEventHandler<TEvent> arayüzünü implemente eden bir sınıf. C#public class BookCreatedEventHandler : IEventHandler<BookCreatedEto>, ITransientDependency { public async Task HandleEventAsync(BookCreatedEto eventData) { // Olay meydana geldiğinde yapılacak işlemler Console.WriteLine($"Yeni kitap oluşturuldu: {eventData.BookName} (ID: {eventData.BookId})"); // Örneğin, bir loglama, bildirim gönderme veya başka bir işlemi tetikleme await Task.CompletedTask; } }

Validation Kuralları

Domain katmanında iş kurallarını (business rules) ve validasyonları uygulamak, uygulamanızın veri bütünlüğünü sağlar. ABP, FluentValidation gibi popüler kütüphanelerle entegre edilebilir veya basit if blokları ve BusinessException ile kendi validasyonlarınızı uygulayabilirsiniz.

C#

using Volo.Abp; // BusinessException için

public class Book : AggregateRoot<Guid>
{
    public void SetPrice(float price)
    {
        if (price <= 0)
        {
            throw new BusinessException(MyProjectDomainErrorCodes.BookPriceMustBePositive);
        }
        Price = price;
    }
}

Value Objects Kullanımı

Value Object (Değer Nesnesi), kimliği olmayan, sadece değerleri ile tanımlanan bir nesne türüdür. İki değer nesnesi, değerleri aynıysa eşittir. Örnek olarak bir adres, para birimi veya renk gibi kavramlar değer nesneleri olabilir.

ABP’de değer nesneleri için özel bir taban sınıf yoktur, ancak .NET record tipleri veya özel bir sınıf ile implemente edilebilirler.

C#

// Örnek bir Value Object
public class Address
{
    public string Street { get; private set; }
    public string City { get; private set; }
    public string ZipCode { get; private set; }

    public Address(string street, string city, string zipCode)
    {
        Street = street;
        City = city;
        ZipCode = zipCode;
    }

    // Değer karşılaştırması için eşitlik (Equality) override edilmeli
    public override bool Equals(object obj)
    {
        if (obj == null || GetType() != obj.GetType())
        {
            return false;
        }
        var other = (Address)obj;
        return Street == other.Street && City == other.City && ZipCode == other.ZipCode;
    }

    public override int GetHashCode()
    {
        return HashCode.Combine(Street, City, ZipCode);
    }
}

Bu değer nesnesini bir entity içinde kullanabilirsiniz:

C#

public class Author : AggregateRoot<Guid>
{
    public string Name { get; set; }
    public Address HomeAddress { get; set; } // Adres bir değer nesnesi
}

Specification Pattern Uygulaması

Specification Pattern (Şartname Deseni), iş kurallarını sorgu mantığından ayırmak için kullanılır. Belirli bir iş kuralını karşılayan entity’leri filtrelemek için esnek ve yeniden kullanılabilir yollar sağlar.

ABP, doğrudan bir “Specification” yapısı sağlamaz, ancak Expression kullanarak veya özel IQueryable uzantıları yazarak bu deseni uygulayabilirsiniz.

C#

using System;
using System.Linq.Expressions;
using Volo.Abp.Domain.Entities;

// Örnek bir spesifikasyon (bir sınıf içinde de tutulabilir)
public static class BookSpecifications
{
    public static Expression<Func<Book, bool>> IsScienceFiction()
    {
        return book => book.Type == BookType.ScienceFiction;
    }

    public static Expression<Func<Book, bool>> PublishedBefore(DateTime date)
    {
        return book => book.PublishDate < date;
    }
}

Bu spesifikasyonlar repository sorgularında kullanılabilir:

C#

var scienceFictionBooks = await _bookRepository.GetListAsync(
    BookSpecifications.IsScienceFiction()
);

Repository Pattern ve IRepository Interface’i

Repository Pattern (Depo Deseni), veri erişim mantığını domain katmanından soyutlar. Entity’lerin veri depolama mekanizmasından habersiz olmasını sağlar. ABP Framework, varsayılan olarak EF Core tabanlı bir repository implementasyonu sunar.

ABP’de IRepository<TEntity, TKey> arayüzü temel repository operasyonlarını (CRUD, sorgulama) sağlar:

C#

using Volo.Abp.Domain.Repositories;
using MyProject.Books;

namespace MyProject.Domain.Repositories
{
    public interface IBookRepository : IRepository<Book, Guid>
    {
        // Özel sorgular buraya eklenebilir
        Task<Book> FindByNameAsync(string name);
        Task<List<Book>> GetBooksByTypeAsync(BookType type);
    }
}

ABP, IRepository arayüzünü otomatik olarak implemente eder, bu nedenle çoğu durumda özel bir implementasyon yazmanıza gerek kalmaz. Ancak, daha karmaşık veya özel sorgulara ihtiyacınız varsa, kendi repository implementasyonunuzu yazabilirsiniz.

C#

// BookRepository implementasyonu (TodoApp.EntityFrameworkCore projesinde)
using System;
using System.Linq;
using System.Threading.Tasks;
using Microsoft.EntityFrameworkCore;
using Volo.Abp.Domain.Repositories.EntityFrameworkCore;
using Volo.Abp.EntityFrameworkCore;
using MyProject.Books;

namespace MyProject.EntityFrameworkCore.Repositories
{
    public class EfCoreBookRepository : EfCoreRepository<MyProjectDbContext, Book, Guid>, IBookRepository
    {
        public EfCoreBookRepository(IDbContextProvider<MyProjectDbContext> dbContextProvider)
            : base(dbContextProvider)
        {
        }

        public async Task<Book> FindByNameAsync(string name)
        {
            return await GetQueryable()
                .Where(b => b.Name == name)
                .FirstOrDefaultAsync();
        }

        public async Task<List<Book>> GetBooksByTypeAsync(BookType type)
        {
            return await GetQueryable()
                .Where(b => b.Type == type)
                .ToListAsync();
        }
    }
}

Bu bölüm, domain katmanınızı güçlü ve sürdürülebilir bir şekilde inşa etmeniz için gerekli temel yapı taşlarını sunar.

Recommended Posts

ABP Framework ile Adım Adım Yazılım Geliştirme – Bölüm 7: Kullanıcı Arayüzü Geliştirme

Uygulamanın kullanıcılarla etkileşim kurduğu katman olan Kullanıcı Arayüzü (UI) Katmanı, genellikle ASP.NET Core MVC (Razor Pages), Angular veya Blazor gibi teknolojilerle geliştirilir. ABP Framework, seçtiğiniz UI teknolojisiyle sorunsuz entegrasyon sağlayarak geliştirme sürecinizi hızlandırır ve ortak UI ihtiyaçlarını karşılar. Bu bölümde, ABP ile kullanıcı arayüzü geliştirmenin temel yönlerini ele alacağız. Seçilen UI Teknolojisine Göre Temel Yapılandırma […]

Admin 

ABP Framework ile Adım Adım Yazılım Geliştirme – Bölüm 6: API Katmanı Geliştirme

Modern uygulamalarda API (Application Programming Interface) katmanı, hem istemci uygulamalar (mobil, web) hem de diğer servislerle iletişim kurmak için kritik öneme sahiptir. ABP Framework, güçlü ve kolay yapılandırılabilir bir API katmanı oluşturmanızı sağlar. Bu bölümde, ABP’nin API geliştirme özelliklerini ve en iyi pratikleri inceleyeceğiz. Auto API Controller Özellikleri ABP Framework’ün en kullanışlı özelliklerinden biri Auto […]

Admin 

ABP Framework ile Adım Adım Yazılım Geliştirme – Bölüm 5: Application Layer Geliştirme

Application Layer (Uygulama Katmanı), uygulamanın kullanım senaryolarını (use cases) ve kullanıcı hikayelerini yönetir. Domain katmanındaki işlevleri kullanarak kullanıcı arayüzünden gelen isteklere yanıt verir ve sunum katmanı ile domain katmanı arasındaki bir köprü görevi görür. Bu bölümde, uygulama katmanının temel bileşenlerini ve geliştirme pratiklerini ele alacağız. Application Service’ler Oluşturma Application Service (Uygulama Servisi), dış dünyadan (UI, […]

Admin 

Leave A Comment