packages/core directory houses the central business logic, decoupled from any specific UI implementation.
| Folder | Responsibility | Key Technologies / Patterns |
|---|---|---|
actions/ | Server-side functions callable from client. Validates input, instantiates and starts the appropriate XState machine, awaits its final result. | Next.js Server Actions |
machines/ | Define and manage complex stateful workflows (State Pattern). Orchestrates the business logic flow, preparing DTOs, invoking methods on resolved Service implementations, and processing output DTOs. | XState |
services/ | Encapsulate discrete domain operations via a structured Service Layer (Strategy Pattern). Implements specific tasks (DB interaction, API calls) invoked by machines, using the contract defined by its interface and method DTOs. | TypeScript Interfaces/Classes. Interface & Method DTOs defined in services/[domain]/domain/. Implementations in services/[domain]/implementations/. |
types/ | Define shared base data structures (Entities), common utility types, and types for XState machines, organized into subdirectories (common/, entities/, machines/). Excludes service method DTOs. | TypeScript |
validation/ | Define data validation schemas, primarily used by actions/ before starting a machine. | Yup (InferType) |
Service Layer Structure (packages/core/services/[domain]/)
Services provide concrete implementations of specific business operations, invoked by state machines. The structure within each service domain directory (e.g., packages/core/services/user/) follows a clear pattern promoting the separation of abstraction from implementation:
-
domain/: This directory defines the Abstraction Layer for the service. It contains only:[Domain]Service.ts: The TypeScriptinterfacedefining the service contract (e.g.,UserService). This specifies what operations the service provides.[Domain]Types.ts: Defines the Data Transfer Objects (DTOs) – the specific TypeScript types/interfaces for the input parameters and return values of each method declared in the[Domain]Service.tsinterface (e.g.,CreateUserMethodInput,CreateUserMethodOutput). This specifies the shape of the data the service works with at its boundary.- Crucially, this folder does not contain any implementation logic or reference types from other layers like validation directly.
-
implementations/: Contains the concrete Strategy classes that implement the[Domain]Service.tsinterface. Each file typically represents a different strategy or version of the service logic (e.g.,DefaultUserService.ts,AdminUserService.ts). This is how the service operations are performed, potentially mapping input DTOs to internal types if needed. -
serviceMap.ts: Implements a Factory Method or Service Locator pattern. It typically exports a function (e.g.,getUserService) that takes criteria (like a client ID, user role, feature flag) and returns the appropriate concrete service instance from theimplementations/directory. This map or its getter function needs to be accessible to the XState machine (e.g., passed via context or imported directly if appropriate). -
index.ts: A simple barrel file that exports the public API of the service module, primarily the factory/locator function fromserviceMap.ts(e.g.,export { getUserService } from './serviceMap';).
Core Types (packages/core/types/)
The types/ directory is crucial for maintaining consistency and enabling static analysis across the core logic.
packages/core/types/ Structure
- Entities: Represent the core data structures of the application (e.g., a
Userobject matching the database schema). - Machine Types: Define the
Contextshape and possibleEventsfor each XState machine, ensuring type safety during state transitions and logic execution. These are kept separate from Service DTOs. - Common Types: Utility types used across different domains (e.g., standardized API response wrappers, pagination structures).
Validation (packages/core/validation/)
All input coming from the client into a Server Action must be validated before processing.
- Uses Yup for schema definition and validation.
- Schemas are defined in
packages/core/validation/, typically organized by domain (e.g.,userSchema.ts). - Server Actions import and use these schemas to validate input.
- Use
yup.InferType<typeof yourSchema>to derive TypeScript types directly from schemas for use in Actions and potentially as the initial input type for Machines (validatedInput). - Validation errors should result in an early return from the Action with a structured error response (see
ActionResultin the Core Workflow Guide).