Architecture: Services & Actions
Adonis EOS follows a clean architecture pattern, separating business logic from controllers into Services and Actions.
1. Services (app/services/)
Services are singleton classes (or object exports) responsible for domain-specific logic, data retrieval, and complex calculations. They are intended to be stateful (within the request lifecycle) or provide utility registries.
Key Patterns
Registries: Many services act as registries for code-first definitions (e.g.,
ModuleRegistry,PostTypeRegistry).Dependency Injection: Services are typically imported directly. Since they are often singletons, they provide a consistent API across the application.
Cross-Service Communication: Services often call each other (e.g.,
PostRenderingServiceusesTokenService).
Common Services
Service | Responsibility |
|---|---|
| Manages registration and configuration of post types. |
| Handles the server-side rendering logic for modules. |
| Manages file uploads, variant generation, and storage providers. |
| Centralizes RBAC and permission checks. |
| Resolves |
| Central registry for all available modules. |
| Central registry for post type configurations. |
2. Actions (app/actions/)
Actions are stateless, single-purpose classes that represent a "unit of work" or a specific mutation in the system. They typically follow the "Command" pattern.
Why use Actions?
Auditability: Actions make it easy to see every possible mutation in the system.
Transactionality: Actions often wrap their logic in a database transaction.
Reusability: An action like
CreatePostActioncan be called from a REST controller, a CLI command, or an AI Agent.
Implementation Example
// app/actions/posts/approve_review_draft.ts
export default class ApproveReviewDraftAction {
async handle(postId: string, userId: string) {
return await db.transaction(async (trx) => {
// 1. Fetch post and review draft
// 2. Promote review draft to source fields
// 3. Promote module staging
// 4. Create a 'published' revision snapshot
// 5. Trigger 'post.published' workflow
})
}
}
Common Actions
Domain | Action | Description |
|---|---|---|
Posts |
| Creates a new post with optional module seeding. |
| Updates core post fields and stages metadata changes. | |
| Saves a snapshot of post + modules into | |
| Promotes review draft to live source fields. | |
Media |
| Handles file upload, optimization, and variant generation. |
| Replaces light or dark base files for an existing asset. | |
| Consolidated logic for bulk deletes, optimizes, and variants. | |
| Safe deletion with usage checking. | |
Translations |
| Clones a post into a new locale with optional structure sync. |
3. Recommended Flow
When adding a new feature:
Identify the Domain: Does it belong in an existing service, or do you need a new one?
Stateless Mutation?: If you are changing data, create an Action.
Complex Retrieval?: If you are fetching or transforming data for display, add a method to a Service.
Keep Controllers Thin: Controllers should only handle request validation, calling the appropriate Service/Action, and returning the response.
4. Lifecycle Hooks
Many actions trigger events that are picked up by the Workflows system. Always ensure that major mutations trigger the corresponding event to maintain system-wide automation.