You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

在领域驱动设计(DDD)中,“查找或创建”逻辑应置于何处?

Great question—this is a super common pattern, and where you place this logic depends a bit on your architecture and how you want to keep your code organized. Let’s break down the most sensible options:

1. Inside the ObjectARepository (Most straightforward for simple creation)

Since you’re already using the repository to look up ObjectA instances, adding a dedicated findOrCreateByPropertyX method here is a natural fit. This keeps all data-related logic for ObjectA centralized.

Example code:

class ObjectARepository {
    // Your existing lookup method
    public function findByPropertyX(string $value): ?ObjectA {
        // Existing logic to fetch by Property X
    }

    public function findOrCreateByPropertyX(string $value): ObjectA {
        // First try to find the existing object
        $objectA = $this->findByPropertyX($value);
        if ($objectA) {
            return $objectA;
        }

        // Handle complex creation logic here
        $objectA = new ObjectA();
        $objectA->setPropertyX($value);
        // Add any extra setup: related entities, default values, validation checks
        // ...

        $this->persist($objectA);
        $this->flush(); // If using Doctrine or similar ORM

        return $objectA;
    }
}

Pros: Keeps all ObjectA data access/modification logic in one place, follows the single responsibility principle (SRP) for the repository, and simplifies calls from your controllers/services.
Caveat: If your creation logic relies on external services (like API calls, other repositories, or validators), avoid turning the repo into a "god object"—consider the next option instead.

2. In a Dedicated Service Class (Best for complex creation flows)

If creating ObjectA involves multiple dependencies (e.g., calling an external API, validating against business rules, or interacting with other entities), a dedicated service class (like ObjectAService or ObjectAManager) is the cleaner choice. This keeps your repository focused solely on data access, while the service handles business logic.

Example code:

class ObjectAService {
    public function __construct(
        private ObjectARepository $objectARepo,
        private ExternalDataService $externalService,
        private ValidatorInterface $validator
    ) {}

    public function findOrCreateByPropertyX(string $value): ObjectA {
        $objectA = $this->objectARepo->findByPropertyX($value);
        if ($objectA) {
            return $objectA;
        }

        // Complex creation with all required dependencies
        $objectA = new ObjectA();
        $objectA->setPropertyX($value);
        $objectA->setRelatedData($this->externalService->fetchRequiredData());
        
        // Validate business rules before saving
        $validationErrors = $this->validator->validate($objectA);
        if (count($validationErrors) > 0) {
            throw new InvalidObjectException($validationErrors);
        }

        $this->objectARepo->persist($objectA);
        $this->objectARepo->flush();

        return $objectA;
    }
}

Pros: Separates concerns perfectly, makes testing easier (you can mock dependencies), and scales well as your creation logic grows more complex. This is my go-to recommendation for non-trivial "find or create" flows.

3. In the ObjectA Domain Model (Use sparingly)

If the creation logic is tightly tied to ObjectA's core behavior, you could add a static factory method to the model itself. However, be careful here—you don’t want to couple your domain model to the repository layer (which violates layered architecture principles).

Example code (only for simple cases):

class ObjectA {
    public static function findOrCreateByPropertyX(string $value, ObjectARepository $repo): ObjectA {
        $objectA = $repo->findByPropertyX($value);
        if ($objectA) {
            return $objectA;
        }

        $objectA = new self();
        $objectA->setPropertyX($value);
        // Keep setup logic minimal here—avoid external dependencies
        $repo->persist($objectA);
        $repo->flush();

        return $objectA;
    }
}

Cons: Couples your domain model to data access logic, which makes it harder to test and refactor. Only use this for very simple creation flows where the logic is intrinsic to ObjectA.

Final Quick Tip

Whatever you do, avoid putting this logic directly in a controller—this bloats controllers with business logic, making them hard to maintain and test.

内容的提问来源于stack exchange,提问作者Hector Ordonez

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.20 11:17:01