İçeriği Atla

PHP ile Domain-Driven Design (DDD) yaklaşımını kullanarak yazılım mimarisi nasıl oluşturulur?

PHP ile Domain-Driven Design (DDD) yaklaşımını kullanarak yazılım mimarisi nasıl oluşturulur?

PHP ile Domain-Driven Design (DDD) yaklaşımını kullanarak bir yazılım mimarisi oluştururken, DDD’nin temel kavramlarını uygulayarak alan odaklı bir yapı geliştirmek önemlidir. Bu yaklaşım, yazılım mimarisini iş mantığı etrafında şekillendirir ve karmaşık alanları yönetilebilir hale getirir.

1. Temel Yapıyı Kurma: Katmanlar ve Bounded Context

DDD mimarisinde, sistemin farklı iş alanlarını temsil eden bağımsız bölümler oluşturulur. Bu bölümler "Bounded Context" olarak adlandırılır ve her biri kendi iş mantığıyla sınırlandırılmış birer alandır.

PHP’de DDD yapısını oluşturmak için projeyi birkaç ana katmana ayırabiliriz:

  • Domain Layer: İş mantığını içerir.
  • Application Layer: Domain katmanını kullanarak uygulama akışını yönetir.
  • Infrastructure Layer: Veri erişimi, ağ bağlantıları vb. dış bağımlılıkları içerir.
  • User Interface Layer: Kullanıcı arayüzü ve API'leri içerir.
src/
├── Domain/
│   ├── Model/
│   ├── Service/
│   └── Repository/
├── Application/
│   ├── Service/
│   └── Command/
├── Infrastructure/
│   ├── Persistence/
│   └── Messaging/
└── UI/
    └── Controller/

2. Domain Katmanı: Model, Value Object, ve Aggregate Root

Domain katmanında, uygulamanın iş mantığını oluşturan bileşenler tanımlanır. Bu bileşenler şunlardır:

  • Entity: Kimliğe sahip iş nesneleridir. Örneğin, User, Product gibi.
  • Value Object: Kimliği olmayan, salt değer içeren nesnelerdir. Örneğin, Address, Money.
  • Aggregate: Domain nesnelerini yönetmek için kullanılan kök nesne. Her Aggregate bir Aggregate Root ile başlar.
namespace App\Domain\Model\User;


class User
{
    private UserId $id;
    private string $name;


    public function __construct(UserId $id, string $name)
    {
        $this->id = $id;
        $this->name = $name;
    }


    public function rename(string $newName): void
    {
        $this->name = $newName;
    }


    // Getters...
}

3. Repository: Veri Erişimini Soyutlama

DDD, veri tabanına doğrudan erişmek yerine Repository arayüzü kullanarak veri erişimini soyutlamayı önerir. Repository, veri tabanıyla ilgili işlemleri kapsülleyerek bağımsız bir veri erişim katmanı sağlar.

namespace App\Domain\Repository;


use App\Domain\Model\User\User;


interface UserRepository
{
    public function save(User $user): void;
    public function findById(UserId $userId): ?User;
}

Infrastructure katmanında ise bu arayüzün bir uygulamasını yaparız:

namespace App\Infrastructure\Persistence;


use App\Domain\Repository\UserRepository;
use App\Domain\Model\User\User;


class MySQLUserRepository implements UserRepository
{
    public function save(User $user): void
    {
        // PDO veya ORM ile veritabanına kaydetme işlemi yapılır
    }


    public function findById(UserId $userId): ?User
    {
        // Veritabanından kullanıcıyı çekme işlemi
    }
}

4. Domain Servisleri: İş Mantığını Modülerleştirme

Bazı iş mantıkları, birden fazla nesneyi veya işlemi kapsar. Bu tür işlemleri Domain Servislerinde toplarız.

namespace App\Domain\Service;


use App\Domain\Model\User\User;
use App\Domain\Repository\UserRepository;


class UserRegistrationService
{
    private UserRepository $userRepository;


    public function __construct(UserRepository $userRepository)
    {
        $this->userRepository = $userRepository;
    }


    public function register(string $name): User
    {
        $user = new User(UserId::generate(), $name);
        $this->userRepository->save($user);


        return $user;
    }
}

5. Uygulama Katmanı: İş Akışlarını Yönetme

Uygulama katmanı, Domain katmanını kullanarak sistemin iş akışlarını yönetir. Bu katman, Domain katmanından iş mantığını alır ve bir veya daha fazla Domain Servisini çalıştırır.

namespace App\Application\Service;


use App\Domain\Service\UserRegistrationService;


class RegisterUserCommand
{
    public string $name;


    public function __construct(string $name)
    {
        $this->name = $name;
    }
}


class UserApplicationService
{
    private UserRegistrationService $registrationService;


    public function __construct(UserRegistrationService $registrationService)
    {
        $this->registrationService = $registrationService;
    }


    public function handle(RegisterUserCommand $command): void
    {
        $this->registrationService->register($command->name);
    }
}

6. Controller Katmanı: Kullanıcı İsteklerini Karşılama

Bu katmanda, kullanıcı istekleri (örneğin HTTP istekleri) işlenir ve uygun komut veya sorgu kullanılarak uygulama katmanındaki iş mantığı çalıştırılır.

namespace App\UI\Controller;


use App\Application\Service\UserApplicationService;
use App\Application\Service\RegisterUserCommand;


class UserController
{
    private UserApplicationService $userService;


    public function __construct(UserApplicationService $userService)
    {
        $this->userService = $userService;
    }


    public function registerUser()
    {
        $command = new RegisterUserCommand($_POST['name']);
        $this->userService->handle($command);
    }
}

7. Bağımlılıkları Yönetme

DDD mimarisinde, bağımlılıkları Dependency Injection (DI) ile yönetiriz. Çoğu PHP framework (Laravel, Symfony), DI desteği sağlar. Ayrıca, Container kullanarak bağımlılıkları kolayca yönetebilirsiniz.

8. Test Etme

Her katman bağımsız olduğu için, birimleri test etmek kolaydır. Örneğin:

  • Domain katmanındaki Entity ve Value Object’leri birim testleriyle test edebilirsiniz.
  • Application katmanındaki servisler için entegrasyon testleri yazabilirsiniz.