Domain-driven Design: Application services vs. Domain services
When to put your logic into Application services vs. when to Domain services. Let's see what ChatGPT says about this.

Today I got a good answer from ChatGPT, with this prompt:
Back to code separation in the different layers. I'm struggling to decide where to put logic and how to draw the line between "services" inside the application layer and something inside the domain layer. E.g. I want to build an AI-Tool integration. The infrastructure layer contains the API communication to the external AI provider (like OpenAI). The user-interface layer contains a controller that enables my users to do 'enhance-item' action which involves the AI. The ui-layer calls a service from the application layer... the application layer consumes the infrastructure layer with the AI and connects the user request and the AI and returns the results (with little logic) to the UI. ... as you see, no domain-layer involed so far?!
As you may notice, I refer to a long conversation with ChatGPT about Laravel and Domain-driven Design.
ChatGPT said:
You're absolutely on the right track—this is a classic case where the line between domain and application logic gets blurry, especially with things like AI integrations that feel valuable but might not represent core domain behavior.
Let’s break it down in terms of DDD + Clean Architecture thinking:
✅ Your current layering sounds like this:
- Infrastructure Layer
Handles OpenAI API calls → perfect place. - Application Layer
Has anEnhanceItemService
or similar → orchestrates request + AI interaction → returns enriched content → ✅ great fit for app logic. - User Interface Layer
A controller receives the HTTP request and calls the application service → exactly what it should do.
❓ But where's the Domain Layer?
If the "enhance-item" logic is purely technical (e.g., transforming text, talking to an external API), and not based on your business rules or domain models, then:
✅ It's okay for the Domain Layer not to be involved.
Domain logic comes in only when:
- You validate or mutate core domain models (like
Item
,ProductDescription
, etc.). - You enforce business rules (e.g., “Items of type X must not be auto-enhanced unless approved by admin”).
If enhancing an item becomes a strategic part of your business, and you start:
- Storing the enhanced version,
- Validating changes,
- Auditing or versioning,
- Interacting with other domain concepts,
… then move some parts into the Domain Layer, e.g. a ContentEnhancer
domain service.
✳️ When to push into Domain Layer?
Use the test:
“Would I want to unit test this logic without infrastructure or Laravel?”
If yes → it's domain.
If not → it's app or infra.
🧠 Summary
Layer | What it does in your case | Status |
---|---|---|
UI | Accepts request, calls app service | ✅ Correct |
Application | Coordinates AI service + input/output | ✅ Correct |
Infrastructure | Talks to OpenAI or similar | ✅ Correct |
Domain | Not involved unless you want to model “enhancement” logic as business concern |
✅ Also fine |
✅ Allowed Call Directions
User Interface → Application → Domain
↘
Infrastructure
- UI Layer
- Calls Application Services.
- Application Layer
- Orchestrates use cases.
- Calls Domain Layer (to use entities, domain services, value objects, etc.).
- Calls Infrastructure Layer via interfaces (e.g.,
AiEnhancerInterface
,ItemRepositoryInterface
).
- Domain Layer
- Isolated.
- Never calls up to application or infrastructure.
- Contains business logic (entities, domain services, value objects).
- Depends on nothing.
- Infrastructure Layer
- Implements interfaces from the Domain or Application layer.
- Called by the Application Layer.
- Never calls back up.
📌 Example: AI Enhancement Use Case
Controller (UI)
└──> EnhanceItemService (Application)
├──> ItemEnhancer (Domain Service)
│ └──> applies business rules, maybe uses ValueObjects
└──> AiEnhancerInterface (in Domain or App)
└──> OpenAiEnhancer (Infrastructure)
🔁 Inversion of Control
- Application depends on interfaces, not implementations.
- Infrastructure implements the interfaces (injected via Laravel container or ServiceProvider).
🧠 Rule of Thumb
Layer | Calls Which Layers? |
---|---|
UI | Application |
Application | Domain & Infrastructure (via interface) |
Domain | Itself only |
Infrastructure | Nothing (called via interface only) |
🔌 Where Should Interfaces Live?
Interfaces that are implemented in the infrastructure layer but used in the application layer should not live in the infrastructure. Why?
Because the higher-level layers (Application, Domain) define what they need — the lower-level Infrastructure just provides how.
✅ Recommended Location for Interfaces
Interface Type | Best Location | Reason |
---|---|---|
External service contracts | Application\Services\*Interface | Application defines the use case; infra provides implementation. |
Repositories | Domain\Repositories\*Interface | Repositories are domain concepts. |
Shared cross-domain contracts | Domain\Shared\Contracts | For interfaces reused by multiple bounded contexts. |
📦 Example (AI Enhancer)
- You want to enhance an item using OpenAI.
- You define this in the Application Layer:
<?php
// src/Domain/Ai/Application/Services/AiEnhancerInterface.php
interface AiEnhancerInterface {
public function enhance(string $text): string;
}
Then you implement it in the infrastructure:
<?php
// src/Domain/Ai/Infrastructure/External/OpenAi/OpenAiEnhancer.php
class OpenAiEnhancer implements AiEnhancerInterface {
public function enhance(string $text): string {
// API call logic
}
}
Bind it in the service provider:
<?php
$this->app->bind(AiEnhancerInterface::class, OpenAiEnhancer::class);
🧭 Rule of Thumb
“The interface always belongs to the client — not the implementer.”
So:
✅ Application/Domain define interfaces
✅ Infrastructure implements them
Comments ()