Carbonteq
Best Practices/Backend/Architecture/Application

Overview

The application layer orchestrates domain entities and infrastructure repositories through workflows that handle validation, business logic coordination, and error boundaries.

Application Layer

The application layer sits between your domain entities and infrastructure repositories, orchestrating business operations through workflows. Unlike use cases, workflows emphasize the flow of data and operations through validated pipelines using Effect's functional composition.


Complete Host Workflow Example

The following comprehensive example demonstrates all application layer patterns in action - from DTO creation through full workflow implementation:

1
Schema-Based DTOs Setup and Foundation

We'll use schema-based DTOs instead of traditional class-based DTOs for better type safety and validation.

2
Create Host DTO - Required and Optional Fields

Build the creation DTO with required userId and optional profile fields. Notice we're composing with existing HostSchema.fields rather than redefining validation rules.

3
Update Profile DTO - Partial with Required ID

Create a partial update schema that picks specific fields and requires an ID. See Schema pick and extend function in Effect Schema API.

4
Specialized Update DTOs - Address and Social Links

Add focused update schemas for address and social links.

5
Simple Operation DTOs - Remove and Fetch

Add minimal DTOs for basic operations.

6
Query DTO - Search and Pagination

Complete the DTO collection with a flexible query schema.

// ❌ Traditional class-based DTO approach
// class CreateHostDTO {
// constructor(
// public userId: string,
// public dob?: Date,
// public phoneNumber?: string
// ) {}
// }
// ✅ Schema-based DTO approach we'll implement
import { HostSchema } from "@domain/entities/user/host.entity";
import { StringToUUID } from "@domain/utils/refined.types";
import { Schema as S } from "effect";
/**
* Schema-based DTOs provide:
* 1. Runtime validation with compile-time types
* 2. Automatic encoding/decoding transformations
* 3. Composable schema building blocks
* 4. Better error handling and validation messages
* 5. Seamless integration with Effect workflows
*
* DTO schemas for host workflow operations
* Following schema-derived pattern for consistency and validation
*/

1
Workflow Foundation - DI and Imports

Start with the essential workflow setup and dependency injection.

2
Private Helper - Initialize Host Data

Add the data initialization helper that handles defaults and timestamps.

3
Create Host Workflow - Validation Pipeline

Build the create operation with DTO validation and entity creation.

4
Update Profile Workflow - Fetch and Merge Pattern

Add the profile update workflow that fetches existing data and merges changes.

5
Specialized Update Workflows - Address and Social Links

Add focused update workflows for specific value objects.

6
Simple Operations - Remove and Get Host

Add straightforward CRUD operations with clean pipelines.

7
Fetch Operations - By User and Query

Add fetch operations that handle Option mapping and complex queries.

8
Utility Operations - Profile Checks and Complete Profiles

Complete the workflow with utility methods for profile management.

import "reflect-metadata";
import { Host } from "@domain/entities/user/host.entity";
import { HostNotFoundError, HostValidationError } from "@domain/entities/user/host.error";
import { PaginationOptions } from "@domain/utils/pagination";
import { DateTime, UUID } from "@domain/utils/refined.types";
import { TOKENS } from "@infra/di/container";
import { Effect as E, Option as O, pipe, Schema as S } from "effect";
import { inject, injectable } from "tsyringe";
/**
* Host workflow for handling host-related business operations
* Manages host profile creation, updates, and queries
* Coordinates between host entity and value objects (Address, SocialLinks)
*/
@injectable()
Enables DI container registration
export class HostWorkflow {
constructor(
@inject(TOKENS.HOST_REPOSITORY)
Repository dependency
private readonly hostRepository: HostRepository
) {}
}

Key Lessons

  1. Schema-First Validation: DTOs derive from domain entity schemas using pick, partial, and extend to ensure consistency and reduce duplication while maintaining type safety.

  2. Effect Pipeline Composition: All workflows follow the pattern: validate input → transform data → call domain/repository → handle errors, using pipe for predictable data flow.

  3. Dependency Injection: Workflows receive repository dependencies through constructor injection with @injectable() and @inject() for clean separation and testability.

  4. Error Boundary Management: Clear separation between parse errors (schema validation), domain errors (business rules), and repository errors (persistence), with explicit Option to error mapping.

  5. Time and Identity Handling: Application layer manages createdAt/updatedAt timestamps and UUID validation at workflow boundaries, keeping domain entities pure.

  6. Fetch-Merge Pattern: Update operations fetch existing entities, serialize to get current state, merge changes, and recreate entities to ensure all domain validations run.

  7. Default Value Initialization: Private helpers provide sensible defaults for complex value objects (Address, SocialLinks) when not provided in requests.

  8. Repository Method Coordination: Workflows orchestrate multiple repository calls (fetchById, update, fetchByQuery) while maintaining transactional consistency through Effect composition.