Service Layer Architecture
The service layer provides a clean separation between business logic and presentation concerns. Services encapsulate domain-specific operations, validation rules, and data transformations, acting as the primary interface between controllers/routes and the data access layer.Key Responsibilities
- Business Logic: Encapsulates domain-specific rules and operations
- Validation: Enforces data integrity and business constraints
- Transaction Management: Handles database operations and commits
- Error Handling: Raises domain-specific exceptions for error conditions
- Data Transformation: Converts between domain models and DTOs
Service Pattern Example
All catalog services follow a consistent pattern. Here’s theColorService as a reference implementation:
app/catalogs/colors/services.py
Common Service Methods
All catalog services implement a standard set of CRUD operations:get_all()
Retrieves all active records from the catalog.create(data: dict)
Creates a new record with validation and conflict detection.data: Dictionary containing model attributes
ValidationError: Invalid or missing required fieldsConflictError: Duplicate name detected
get_by_id(id: int)
Retrieves a specific record by its identifier.id: Primary key identifier
NotFoundError: Record not found
update(id: int, data: dict)
Updates an existing record with validation.id: Primary key identifierdata: Dictionary with updated attributes
NotFoundError: Record not foundValidationError: Invalid dataConflictError: Duplicate name detected
delete(id: int)
Performs soft deletion by marking records as inactive.id: Primary key identifier
NotFoundError: Record not found
Business Logic Separation
Services enforce business rules independently of the presentation layer:Example: Case-Insensitive Uniqueness
Example: Multi-Field Validation
app/catalogs/unit_of_measures/services.py
Example: Soft Delete Pattern
Service Design Principles
- Static Methods: Services use static methods as they don’t maintain state
- Transaction Safety: All writes wrapped in try/except for IntegrityError
- Early Validation: Input validation before database queries
- Consistent Exceptions: Use domain exceptions (ValidationError, NotFoundError, ConflictError)
- Idempotent Reads: get_all() and get_by_id() have no side effects
- Normalized Data: Strip whitespace and normalize before persistence
Available Services
- ColorService - app/catalogs/colors/services.py:13
- WoodTypeService - app/catalogs/wood_types/services.py:11
- UnitOfMeasureService - app/catalogs/unit_of_measures/services.py:13
- RoleService - app/catalogs/roles/services.py:13