This module contains the Application Layer of the IES system, following Clean Architecture and Domain-Driven Design principles.
The Application Services layer orchestrates operations across multiple Bounded Contexts to implement complex business workflows. It acts as a coordination layer between the presentation layer (controllers) and the domain layer (use cases).
- Use Case Orchestration: Combines multiple use cases from different bounded contexts into a single transaction
- Cross-Context Coordination: Coordinates operations between
userrepository-core,security-core, and other core modules - Workflow Management: Implements complex multi-step business processes
- Code Reusability: Provides convenient APIs that can be shared across different presentation layers (GraphQL, REST, CLI)
graph TD
A[Presentation Layer<br/>Controllers] --> B[Application Layer<br/>THIS MODULE ⭐]
B --> C[userrepository-core<br/>Domain Layer]
B --> D[security-core<br/>Domain Layer]
A1[GraphQL Mutations] -.-> A
A2[REST Endpoints] -.-> A
A3[CLI Commands] -.-> A
B1[UserApplicationService] -.-> B
B2[RoleApplicationService] -.-> B
C1[CreateUserUseCase] -.-> C
C2[UpdateUserUseCase] -.-> C
D1[SetPasswordUseCase] -.-> D
D2[AuthenticationUseCase] -.-> D
style B fill:#e1f5ff,stroke:#01579b,stroke-width:3px
style A fill:#f3e5f5,stroke:#4a148c
style C fill:#e8f5e9,stroke:#1b5e20
style D fill:#e8f5e9,stroke:#1b5e20
❌ Not Domain Logic: Application Services do NOT contain business rules. Business logic belongs in Use Cases or Domain Entities.
❌ Not Infrastructure: They do NOT deal with databases, HTTP, or external systems. That's the job of the Infrastructure Layer (adapters).
❌ Not Controllers: They do NOT handle HTTP requests or GraphQL queries. That's the Presentation Layer.
- You need to orchestrate multiple use cases from different bounded contexts
- The same workflow is needed in multiple controllers (GraphQL + REST)
- You need to manage transactional boundaries across contexts
- You want to avoid code duplication between presentation layers
- You only call one single use case → Controllers can call use cases directly
- The operation is bounded-context-specific → Put it in the domain layer
- It contains business logic → That belongs in use cases or entities
User creation requires operations from two different bounded contexts:
- userrepository-core: Create the user entity
- security-core: Set the user's password
Without an application service, every controller would need to duplicate this orchestration:
// ❌ BAD: Code duplication in GraphQL Controller
public String createUser(InputUser user, String password) {
String userId = createUserUseCase.createUser(...); // userrepository-core
setPasswordUseCase.setPassword(userId, password); // security-core
return userId;
}
// ❌ BAD: Same code duplicated in REST Controller
public String createUser(UserDto dto) {
String userId = createUserUseCase.createUser(...); // userrepository-core
setPasswordUseCase.setPassword(userId, dto.password()); // security-core
return userId;
}Application Service orchestrates the workflow once:
sequenceDiagram
participant Controller as GraphQL/REST Controller
participant AppService as UserApplicationService
participant UserUC as CreateUserUseCase
participant PasswordUC as SetPasswordUseCase
Controller->>AppService: createUserWithPassword(request)
AppService->>UserUC: createUser(user, roles)
UserUC-->>AppService: userId
alt password provided
AppService->>PasswordUC: setUserPassword(userId, password)
PasswordUC-->>AppService: void
end
AppService-->>Controller: userId
Note over AppService: Orchestrates 2 bounded contexts:<br/>userrepository + security
// ✅ GOOD: Application Service (in THIS module)
public class UserApplicationService {
public String createUserWithPassword(CreateUserWithPasswordRequest request) {
// 1. Create user (userrepository-core)
String userId = createUserUseCase.createUser(...);
// 2. Set password (security-core)
if (request.password() != null) {
setPasswordUseCase.setUserPassword(...);
}
return userId;
}
}
// ✅ GOOD: Controllers use the Application Service
// GraphQL Controller
public String createUser(InputUser user, String password) {
return userApplicationService.createUserWithPassword(...);
}
// REST Controller
public String createUser(UserDto dto) {
return userApplicationService.createUserWithPassword(...);
}@Inject
private Provider<UserApplicationService> userApplicationServiceProvider;
public String createUser(InputUser inputUser, String password, List<Identifier> roles) {
User user = InputUserTransformer.toUser(inputUser);
return userApplicationServiceProvider
.get()
.createUserWithPassword(
CreateUserWithPasswordRequest.builder()
.user(user)
.password(password)
.roleIdentifiers(roles)
.build());
}graph LR
A[com.sitepark.ies.application] --> B[user/]
A --> C[role/]
A --> D[security/]
B --> B1[UserApplicationService]
B --> B2[CreateUserWithPasswordRequest]
C --> C1[RoleApplicationService<br/>future]
D --> D1[AuthenticationWorkflow<br/>future]
style B fill:#e3f2fd,stroke:#1976d2
style C fill:#fff3e0,stroke:#f57c00
style D fill:#fff3e0,stroke:#f57c00
graph TB
A[ies-application-services<br/>THIS MODULE]
A --> B[ies-userrepository-core]
A --> C[ies-security-core]
A --> D[ies-audit-core]
A --> E[ies-shared-kernel]
F[ies-userrepository-graphql-extension] --> A
G[ies-userrepository-rest-extension] --> A
H[Any Presentation Layer] --> A
style A fill:#e1f5ff,stroke:#01579b,stroke-width:3px
style B fill:#e8f5e9,stroke:#1b5e20
style C fill:#e8f5e9,stroke:#1b5e20
style D fill:#e8f5e9,stroke:#1b5e20
style E fill:#fff9c4,stroke:#f57f17
style F fill:#f3e5f5,stroke:#4a148c
style G fill:#f3e5f5,stroke:#4a148c
style H fill:#f3e5f5,stroke:#4a148c
This module depends on:
- ies-userrepository-core - User management domain
- ies-security-core - Security and authentication domain
- ies-audit-core - Audit logging (optional)
- ies-shared-kernel - Shared domain primitives
This module is used by:
- ies-userrepository-graphql-extension - GraphQL API
- ies-userrepository-rest-extension - REST API (if exists)
- Any other presentation layer that needs user workflows
Each application service focuses on one area of functionality:
UserApplicationService→ User-related workflowsRoleApplicationService→ Role-related workflows
Application services should be thin orchestration layers:
- ✅ Call use cases
- ✅ Pass data between contexts
- ✅ Handle transaction boundaries
- ❌ No business logic
- ❌ No validation (that's in use cases)
- ❌ No domain rules (that's in entities)
Application services are allowed to know about multiple bounded contexts. This is their purpose - to coordinate across contexts where controllers should not.
Application services are tested with mocked use cases:
@Test
void testCreateUserWithPassword() {
// Mock the use cases
when(createUserUseCase.createUser(any())).thenReturn("123");
// Call application service
String userId = service.createUserWithPassword(request);
// Verify orchestration
verify(createUserUseCase).createUser(any());
verify(setPasswordUseCase).setUserPassword(any());
assertEquals("123", userId);
}See UserApplicationServiceTest for complete examples.
"Use cases are the application-specific business rules. Application services orchestrate the flow between use cases."
"Application Services define the jobs the software is supposed to do. They orchestrate domain operations but contain no business logic themselves."
"The application layer coordinates domain operations. It's where you compose use cases to implement higher-level workflows."
- ies-userrepository-core - Domain logic for user management
- ies-security-core - Domain logic for authentication/authorization
- ies-userrepository-graphql-extension - GraphQL presentation layer
- ies-bootstrap - Application startup and dependency injection
If you're unsure whether something belongs in an Application Service:
- Does it orchestrate multiple bounded contexts? → Yes: Application Service
- Is it reused in multiple controllers? → Yes: Application Service
- Does it contain business logic? → No: Move to Use Case
- Is it a single use case call? → No: Call use case directly from controller
Remember: Application Services are orchestration, not implementation. Keep them thin and focused on coordination.