Aracılığıyla paylaş


.NET ile mikro hizmet etki alanı modeli uygulama

İpucu

Bu içerik, .NET Docs'ta veya çevrimdışı olarak okunabilen ücretsiz indirilebilir bir PDF olarak sağlanan Kapsayıcılı .NET Uygulamaları için .NET Mikro Hizmet Mimarisi e-Kitabı'ndan bir alıntıdır.

.NET Microservices Architecture for Containerized .NET Applications eBook cover thumbnail.

Önceki bölümde, etki alanı modeli tasarlamaya yönelik temel tasarım ilkeleri ve desenleri açıklanmıştır. Şimdi .NET (düz C# kodu) ve EF Core kullanarak etki alanı modelini uygulamanın olası yollarını keşfetmenin zamanı geldi. Etki alanı modeliniz yalnızca kodunuzdan oluşur. Yalnızca EF Core model gereksinimlerine sahip olur, ancak EF'e gerçek bağımlılıklar içermez. Etki alanı modelinizde EF Core'a veya başka bir ORM'ye yönelik sabit bağımlılıklarınız veya başvurularınız olmamalıdır.

Özel .NET Standart Kitaplığı'nda etki alanı modeli yapısı

eShopOnContainers başvuru uygulaması için kullanılan klasör kuruluşu, uygulama için DDD modelini gösterir. Farklı bir klasör kuruluşunun uygulamanız için yapılan tasarım seçimlerini daha net bir şekilde bildirdiğini fark edebilirsiniz. Şekil 7-10'da görebileceğiniz gibi, sıralama etki alanı modelinde sipariş toplama ve alıcı toplamı şeklinde iki toplama vardır. Her toplama, bir etki alanı varlıkları ve değer nesneleri grubudur, ancak tek bir etki alanı varlığından (toplam kök veya kök varlık) oluşan bir toplamanız da olabilir.

Screenshot of the Ordering.Domain project in Solution Explorer.

Ordering.Domain projesi için, BuyerAggregate ve OrderAggregate klasörlerini içeren AggregatesModel klasörünü, her birinin varlık sınıflarını, değer nesnesi dosyalarını vb. içeren Çözüm Gezgini görünümü.

Şekil 7-10. eShopOnContainers'da mikro hizmet siparişi için etki alanı modeli yapısı

Ayrıca etki alanı modeli katmanı, etki alanı modelinizin altyapı gereksinimleri olan depo sözleşmelerini (arabirimler) içerir. Başka bir deyişle, bu arabirimler altyapı katmanının uygulaması gereken depoları ve yöntemleri ifade eder. Depoların uygulanması altyapı katmanı kitaplığında etki alanı modeli katmanının dışına yerleştirildiğinden, etki alanı modeli katmanının API veya Entity Framework gibi altyapı teknolojilerinden sınıflar tarafından "kirlenmemesi" kritik önem taşır.

Ayrıca, etki alanı varlıklarınız ve değer nesneleriniz için temel olarak kullanabileceğiniz özel temel sınıfları içeren bir SeedWork klasörü de görebilirsiniz, böylece her etki alanının nesne sınıfında yedekli kodunuz yoktur.

Özel bir .NET Standard kitaplığında yapı toplamaları

Toplama işlemi, işlem tutarlılığını eşleştirmek için birlikte gruplandırılmış bir etki alanı nesneleri kümesini ifade eder. Bu nesneler, varlıkların örnekleri (bunlardan biri toplama kökü veya kök varlığı) ve ek değer nesneleri olabilir.

İşlem tutarlılığı, bir toplama işleminin iş eyleminin sonunda tutarlı ve güncel olması garanti edilir. Örneğin, eShopOnContainers sipariş mikro hizmet etki alanı modelinden alınan sipariş toplamı Şekil 7-11'de gösterildiği gibi oluşturulur.

Screenshot of the OrderAggregate folder and its classes.

OrderAggregate klasörünün ayrıntılı bir görünümü: Address.cs bir değer nesnesi, IOrderRepository bir depo arabirimi, Order.cs toplama kökü, OrderItem.cs bir alt varlık ve OrderStatus.cs bir numaralandırma sınıfıdır.

Şekil 7-11. Visual Studio çözümünde sipariş toplama

Toplama klasöründeki dosyalardan herhangi birini açarsanız, SeedWork klasöründe uygulandığı gibi varlık veya değer nesnesi gibi özel bir temel sınıf veya arabirim olarak nasıl işaretlendiğini görebilirsiniz.

Etki alanı varlıklarını POCO sınıfları olarak uygulama

Etki alanı varlıklarınızı uygulayan POCO sınıfları oluşturarak .NET'te bir etki alanı modeli uygularsınız. Aşağıdaki örnekte Order sınıfı bir varlık ve aynı zamanda bir toplama kökü olarak tanımlanır. Order sınıfı Varlık temel sınıfından türetildiği için varlıklarla ilgili ortak kodu yeniden kullanabilir. Bu temel sınıfların ve arabirimlerin etki alanı modeli projesinde sizin tarafınızdan tanımlandığını unutmayın; bu nedenle bu kod, EF gibi bir ORM'nin altyapı kodu değil, sizin kodunuzdur.

// COMPATIBLE WITH ENTITY FRAMEWORK CORE 5.0
// Entity is a custom base class with the ID
public class Order : Entity, IAggregateRoot
{
    private DateTime _orderDate;
    public Address Address { get; private set; }
    private int? _buyerId;

    public OrderStatus OrderStatus { get; private set; }
    private int _orderStatusId;

    private string _description;
    private int? _paymentMethodId;

    private readonly List<OrderItem> _orderItems;
    public IReadOnlyCollection<OrderItem> OrderItems => _orderItems;

    public Order(string userId, Address address, int cardTypeId, string cardNumber, string cardSecurityNumber,
            string cardHolderName, DateTime cardExpiration, int? buyerId = null, int? paymentMethodId = null)
    {
        _orderItems = new List<OrderItem>();
        _buyerId = buyerId;
        _paymentMethodId = paymentMethodId;
        _orderStatusId = OrderStatus.Submitted.Id;
        _orderDate = DateTime.UtcNow;
        Address = address;

        // ...Additional code ...
    }

    public void AddOrderItem(int productId, string productName,
                            decimal unitPrice, decimal discount,
                            string pictureUrl, int units = 1)
    {
        //...
        // Domain rules/logic for adding the OrderItem to the order
        // ...

        var orderItem = new OrderItem(productId, productName, unitPrice, discount, pictureUrl, units);

        _orderItems.Add(orderItem);

    }
    // ...
    // Additional methods with domain rules/logic related to the Order aggregate
    // ...
}

Bunun POCO sınıfı olarak uygulanan bir etki alanı varlığı olduğunu unutmayın. Entity Framework Core'a veya başka bir altyapı çerçevesine doğrudan bağımlılığı yoktur. Bu uygulama, DDD'de olması gerektiği gibi, yalnızca etki alanı modeli uygulayan C# kodudur.

Buna ek olarak, sınıfı IAggregateRoot adlı bir arabirimle dekore edilmiştir. Bu arabirim, bazen işaretçi arabirimi olarak da adlandırılan ve yalnızca bu varlık sınıfının aynı zamanda bir toplama kökü olduğunu belirtmek için kullanılan boş bir arabirimdir.

İşaretçi arabirimi bazen bir anti-desen olarak kabul edilir; ancak, özellikle de bu arabirim gelişiyor olabilirken sınıfı işaretlemek için de temiz bir yoldur. Bir öznitelik, işaretçi için diğer seçenek olabilir, ancak sınıfın üzerine Bir Toplama öznitelik işaretçisi koymak yerine IAggregate arabiriminin yanında temel sınıfı (Varlık) görmek daha hızlıdır. Her durumda bu bir tercih meselesidir.

Toplu köke sahip olmak, toplama varlıklarının tutarlılığı ve iş kurallarıyla ilgili kodun çoğunun Order toplama kök sınıfında yöntemler olarak uygulanması gerektiği anlamına gelir (örneğin, toplamaya bir OrderItem nesnesi eklerken AddOrderItem). OrderItems nesnelerini bağımsız olarak veya doğrudan oluşturmamalı veya güncelleştirmemelisiniz; AggregateRoot sınıfı, alt varlıklarına karşı herhangi bir güncelleştirme işleminin denetimini ve tutarlılığını korumalıdır.

Etki Alanı Varlıklarındaki verileri kapsülleme

Varlık modellerindeki yaygın bir sorun, koleksiyon gezinti özelliklerini genel olarak erişilebilir liste türleri olarak kullanıma sunmalarıdır. Bu, ortak çalışan geliştiricilerin bu koleksiyon türlerinin içeriğini işlemesine olanak tanır ve bu da koleksiyonla ilgili önemli iş kurallarını atlayabilir ve nesneyi geçersiz durumda bırakabilir. Bunun çözümü, ilgili koleksiyonlara salt okunur erişimi kullanıma sunmak ve istemcilerin bunları işleme yöntemlerini tanımlayan yöntemleri açıkça sağlamaktır.

Önceki kodda, birçok özniteliğin salt okunur veya özel olduğunu ve yalnızca sınıf yöntemleri tarafından güncelleştirilebilir olduğunu unutmayın, bu nedenle tüm güncelleştirmeler iş etki alanı sabit değerlerini ve sınıf yöntemlerinde belirtilen mantığı dikkate alır.

Örneğin, aşağıdaki DDD desenleri, herhangi bir komut işleyicisi yönteminden veya uygulama katmanı sınıfından aşağıdakileri yapmamalısınız (aslında, bunu yapmanız imkansız olmalıdır):

// WRONG ACCORDING TO DDD PATTERNS – CODE AT THE APPLICATION LAYER OR
// COMMAND HANDLERS
// Code in command handler methods or Web API controllers
//... (WRONG) Some code with business logic out of the domain classes ...
OrderItem myNewOrderItem = new OrderItem(orderId, productId, productName,
    pictureUrl, unitPrice, discount, units);

//... (WRONG) Accessing the OrderItems collection directly from the application layer // or command handlers
myOrder.OrderItems.Add(myNewOrderItem);
//...

Bu durumda, Add yöntemi yalnızca OrderItems koleksiyonuna doğrudan erişimi olan veri ekleme işlemidir. Bu nedenle, alt varlıklarla bu işlemle ilgili etki alanı mantığının, kurallarının veya doğrulamalarının çoğu uygulama katmanına (komut işleyicileri ve Web API denetleyicileri) yayılır.

Toplama köküne geçerseniz, toplama kökü sabitlerini, geçerliliğini veya tutarlılığını garanti edemez. Sonunda spagetti kodunuz veya işlem betiği kodunuz olur.

DDD desenlerini izlemek için varlıkların herhangi bir varlık özelliğinde ortak ayarlayıcıları olmamalıdır. Bir varlıktaki değişiklikler, varlıkta gerçekleştirdikleri değişiklik hakkında her yerde açık dile sahip açık yöntemler tarafından yönlendirilmelidir.

Ayrıca, varlık içindeki koleksiyonlar (sipariş öğeleri gibi) salt okunur özellikler olmalıdır (AsReadOnly yöntemi daha sonra açıklanmıştır). Bunu yalnızca toplama kök sınıfı yöntemleri veya alt varlık yöntemleri içinden güncelleştirebilmeniz gerekir.

Order toplama kökü kodunda görebileceğiniz gibi, varlığın verilerine veya alt varlıklarına yönelik tüm işlemlerin varlık sınıfındaki yöntemler aracılığıyla gerçekleştirilmesi için tüm ayarlayıcıların özel veya en azından salt okunur olması gerekir. Bu işlem betiği kodu uygulamak yerine denetimli ve nesne odaklı bir şekilde tutarlılığı korur.

Aşağıdaki kod parçacığı, Order toplamasına OrderItem nesnesi ekleme görevini kodlayabilirsiniz.

// RIGHT ACCORDING TO DDD--CODE AT THE APPLICATION LAYER OR COMMAND HANDLERS
// The code in command handlers or WebAPI controllers, related only to application stuff
// There is NO code here related to OrderItem object's business logic
myOrder.AddOrderItem(productId, productName, pictureUrl, unitPrice, discount, units);

// The code related to OrderItem params validations or domain rules should
// be WITHIN the AddOrderItem method.

//...

Bu kod parçacığında, OrderItem nesnesinin oluşturulmasıyla ilgili doğrulamaların veya mantığın çoğu, özellikle toplamadaki diğer öğelerle ilgili doğrulamalar ve mantık olmak üzere AddOrderItem yönteminde Order toplama kökünün denetiminde olacaktır. Örneğin, AddOrderItem'e birden çok çağrının sonucu olarak aynı ürün öğesini alabilirsiniz. Bu yöntemde, ürün öğelerini inceleyip aynı ürün öğelerini birkaç birim içeren tek bir OrderItem nesnesinde birleştirebilirsiniz. Buna ek olarak, farklı indirim tutarları varsa ancak ürün kimliği aynıysa, büyük olasılıkla daha yüksek indirim uygularsınız. Bu ilke, OrderItem nesnesi için diğer tüm etki alanı mantığı için geçerlidir.

Ayrıca, yeni OrderItem(params) işlemi de Order toplama kökünden AddOrderItem yöntemi tarafından denetlenecek ve gerçekleştirilecektir. Bu nedenle, bu işlemle ilgili mantığın veya doğrulamaların çoğu (özellikle diğer alt varlıklar arasındaki tutarlılığı etkileyen her şey) toplam kök içinde tek bir yerde olur. Bu, toplama kök deseninin nihai amacıdır.

Entity Framework Core 1.1 veya sonraki bir sürümü kullandığınızda, özelliklere ek olarak alanlara eşlemeye izin verdiğinden DDD varlığı daha iyi ifade edilebilir. Bu, alt varlık koleksiyonlarını veya değer nesnelerini korurken kullanışlıdır. Bu geliştirmeyle, özellikler yerine basit özel alanları kullanabilir ve alan koleksiyonuna yönelik herhangi bir güncelleştirmeyi genel yöntemlerle uygulayabilir ve AsReadOnly yöntemi aracılığıyla salt okunur erişim sağlayabilirsiniz.

DDD'de, verilerin sabit ve tutarlılığını denetlemek için varlığı yalnızca varlıktaki (veya oluşturucudaki) yöntemlerle güncelleştirmek istersiniz, bu nedenle özellikler yalnızca get erişimcisiyle tanımlanır. Özellikler özel alanlar tarafından desteklenir. Özel üyelere yalnızca sınıfın içinden erişilebilir. Ancak, bir özel durum vardır: EF Core'un da bu alanları ayarlaması gerekir (böylece nesneyi uygun değerlerle döndürebilir).

Yalnızca veritabanı tablosundaki alanlara erişimcileri alma özelliğiyle eşleme özellikleri

Özellikleri veritabanı tablosu sütunlarına eşlemek bir etki alanı sorumluluğu değil, altyapı ve kalıcılık katmanının bir parçasıdır. Varlıkları nasıl modelleyebileceğinizle ilgili EF Core 1.1 veya sonraki sürümlerdeki yeni özelliklerin farkında olmanız için burada bundan bahsedeceğiz. Bu konudaki ek ayrıntılar altyapı ve kalıcılık bölümünde açıklanmıştır.

EF Core 1.0 veya sonraki bir sürümü kullandığınızda, DbContext içinde yalnızca alıcılarla tanımlanan özellikleri veritabanı tablosundaki gerçek alanlarla eşlemeniz gerekir. Bu, PropertyBuilder sınıfının HasField yöntemiyle yapılır.

Özellikleri olmayan alanları eşleme

EF Core 1.1 veya sonraki sürümlerindeki sütunları alanlarla eşleme özelliği sayesinde özellikleri kullanmamak da mümkündür. Bunun yerine, yalnızca tablodaki sütunları alanlarla eşleyebilirsiniz. Bunun yaygın kullanım örneklerinden biri, varlığın dışından erişilmesi gerekmeyen bir iç durumun özel alanlarıdır.

Örneğin, yukarıdaki OrderAggregate kod örneğinde, alan gibi _paymentMethodId bir ayarlayıcı veya alıcı için ilişkili özelliği olmayan birkaç özel alan vardır. Bu alan siparişin iş mantığı içinde de hesaplanabilir ve siparişin yöntemlerinden kullanılabilir, ancak veritabanında da kalıcı olması gerekir. Dolayısıyla EF Core'da (v1.1'den bu yana) ilgili özelliği olmayan bir alanı veritabanındaki bir sütunla eşlemenin bir yolu vardır. Bu, bu kılavuzun Altyapı katmanı bölümünde de açıklanmıştır.

Ek kaynaklar