Course navigationPhase 14
Volume 04 · Phase 14

Service & Business Logic Architecture

Move orchestration and business invariants into cohesive services/policies so transport and persistence details do not become the domain model.

14lessons + labs
5klesson words
6source references
What you will ship

Phase outcome and project increment

Learning outcome

Move orchestration and business invariants into cohesive services/policies so transport and persistence details do not become the domain model.

Concrete deliverable

Refactored task service with explicit policies/invariants and thin controllers.

Figure 14.1 — Where this phase lives in TaskFlow
validated request
service use case
policy/invariant
repository/Prisma
result/error
Trace the flow left-to-right for implementation and right-to-left for debugging. At every arrow, ask what data crosses the boundary and what proves it is trustworthy.
Instructor mental model

Principles to keep while coding

01

Business rules belong in services or domain-focused collaborators, not duplicated across controllers and components.

02

Keep orchestration separate from low-level persistence details so behavior can be tested clearly.

03

Make rule boundaries explicit before adding abstractions such as repositories.

04

Prefer small cohesive methods that communicate intent over giant service methods that mix validation, authorization, and I/O.

Code-along path

Guided phase lab

Do these in order. Each step should end with evidence before the next one starts.

  1. 01

    Choose one endpoint with mixed concerns and list every responsibility it currently performs.

  2. 02

    Move HTTP-only concerns back to controller/boundary and persistence mechanics behind a focused collaborator.

  3. 03

    Express one business rule with a name that describes the rule, not the framework.

  4. 04

    Add a use-case method whose inputs/outputs make orchestration visible.

  5. 05

    Test the rule without booting the whole web server where practical.

  6. 06

    Review the resulting dependency direction and remove abstractions that add no clarity.

Debugging studio

Failures you should know how to diagnose

Failure drill 1

God services with hundreds of unrelated lines

Predict the symptom, capture evidence at the nearest boundary, change one variable, and verify the same reproduction after the fix.

Failure drill 2

Business rules duplicated in controllers and frontend

Predict the symptom, capture evidence at the nearest boundary, change one variable, and verify the same reproduction after the fix.

Failure drill 3

Repository abstraction added before there is a boundary to protect

Predict the symptom, capture evidence at the nearest boundary, change one variable, and verify the same reproduction after the fix.

Failure drill 4

Methods named only after CRUD verbs when workflow intent matters

Predict the symptom, capture evidence at the nearest boundary, change one variable, and verify the same reproduction after the fix.

Watch-order style

Lessons

The source chapters come first. Every phase now ends with a guided build, a debugging workshop, and an independent challenge so reading turns into transferable skill.

01

Controller vs Service Responsibilities

Controller vs Service Responsibilities Keep HTTP translation thin while providers execute application use cases Phase 14 - Service & Business Logic Architecture Volume 04 - Professional Back

2 min
02

Business Invariants and Rule Ownership

Business Invariants & Rule Ownership Separate transport validation, business rules, authorization, and database constraints Phase 14 - Service & Business Logic Architecture Volume 04 - Profe

2 min
03

Task Status Workflow and Derived State

Task Status Workflow & Derived State Explicit transitions, completedAt consistency, and operations with business meaning Phase 14 - Service & Business Logic Architecture Volume 04 - Professi

2 min
04

Cross Model Rules and Workspace Membership

Cross-Model Rules & Workspace Membership Use relational context to enforce assignee membership invariants Phase 14 - Service & Business Logic Architecture Volume 04 - Professional Backend Ar

2 min
05

Business Validation and Time Based Policies

Business Validation & Time-Based Policies Why semantically valid state needs more than Zod request validation Phase 14 - Service & Business Logic Architecture Volume 04 - Professional Backen

2 min
06

Prisma Transactions and Atomic Workflows

Prisma Transactions & Atomic Workflows Choose nested writes, transaction arrays, or interactive transactions only when a use case needs them Phase 14 - Service & Business Logic Architecture

2 min
07

Service Orchestration and Extraction Thresholds

Service Orchestration & Extraction Thresholds Keep logic readable and know when complexity has earned a new abstraction Phase 14 - Service & Business Logic Architecture Volume 04 - Professio

2 min
08

Anti Patterns and Architecture Theater

Anti-Patterns & Architecture Theater Avoid controller business logic, blind Prisma patches, transaction-everywhere, and premature layers Phase 14 - Service & Business Logic Architecture Volu

2 min
09

Enterprise Transfer HRIS Payroll Procurement Inventory

Enterprise Transfer Map the service-layer mental model to HRIS, payroll, procurement, inventory, and recruitment Phase 14 - Service & Business Logic Architecture Volume 04 - Professional Bac

2 min
10

Hands On Lab and Phase Exit

Hands-On Lab & Phase Exit Workflow rules, membership invariant, transaction reasoning, exercises, and oral exam Phase 14 - Service & Business Logic Architecture Volume 04 - Professional Back

2 min
LAB

Guided Build Lab

Purpose Move orchestration and business invariants into cohesive services/policies so transport and persistence details do not become the domain model.

3 min
LAB

Debugging Workshop

Goal Learn to diagnose this phase's failures systematically instead of reinstalling packages, changing random configuration, or copying a fix without understanding it.

2 min
LAB

Phase Synthesis & Independent Challenge

Mission Rebuild the capability of Phase 14 without following the exact code line-by-line. The goal is transfer: can you use the ideas when the variable names, UI, or route shape changes?

2 min
LAB

Mastery Capstone Lab

Integrate Phase 14 without following a transcript.

2 min
Practice ladder

Exercises that increase independence

Guided

Choose one endpoint with mixed concerns and list every responsibility it currently performs.

Practice

Express one business rule with a name that describes the rule, not the framework.

Debug

reproduce and diagnose — God services with hundreds of unrelated lines.

Independent

deliver Refactored task service with explicit policies/invariants and thin controllers.

Explain-back

Can place a rule in the correct layer and Can keep controllers thin.

Mastery gate

Do not leave the phase only because you reached the bottom

Phase exit criteria

Knowledge check

Scenario and concept quiz

These questions test engineering choices rather than whether you remember a lesson title.

0/0 correct · 0/8 answered
01Which practice best matches Phase 14's engineering goal?
02Which practice best matches Phase 14's engineering goal?
03Which practice best matches Phase 14's engineering goal?
04Which practice best matches Phase 14's engineering goal?
05You are working through the Phase 14 lab. Which next action is the most disciplined step?
06You are working through the Phase 14 lab. Which next action is the most disciplined step?
07You are working through the Phase 14 lab. Which next action is the most disciplined step?
08You are working through the Phase 14 lab. Which next action is the most disciplined step?
Source snapshot

Reference files

Attempt the work first. Use snapshots to compare a checkpoint, not as a substitute for understanding.

reference/TaskFlow_Business_Logic_Snapshot/README.txt
TaskFlow Phase 14 reference snapshot

This snapshot contains only the files changed or emphasized by Phase 14. It assumes the completed Phase 13 project already exists.

Core rules demonstrated:
- Controllers remain thin and delegate use cases to TasksService.
- TasksService owns Task business invariants.
- Task status changes follow an explicit transition policy.
- completedAt is synchronized with DONE/reopen transitions.
- A Task assignee must be a member of the Task Project's Workspace.
- New due dates cannot be in the past (course policy for this application).
- A transaction is used for the explicit assignment workflow because the operation reads membership context and then writes the assignment as one use case.
- We do not introduce Repository Pattern yet; PrismaService remains the persistence boundary for this phase.
reference/TaskFlow_Business_Logic_Snapshot/apps/api/src/common/errors/application.exceptions.ts
import {
  BadRequestException,
  ConflictException,
  NotFoundException,
} from "@nestjs/common";

export class TaskNotFoundException extends NotFoundException {
  constructor(taskId: string) {
    super({
      code: "TASK_NOT_FOUND",
      message: `Task ${taskId} was not found.`,
    });
  }
}

export class ProjectNotFoundException extends NotFoundException {
  constructor(projectId: string) {
    super({
      code: "PROJECT_NOT_FOUND",
      message: `Project ${projectId} was not found.`,
reference/TaskFlow_Business_Logic_Snapshot/apps/api/src/tasks/tasks.controller.ts
import {
  Body,
  Controller,
  Delete,
  Get,
  HttpCode,
  HttpStatus,
  Param,
  Patch,
  Post,
} from "@nestjs/common";
import { StandardSchemaValidationPipe } from "@nestjs/common";
import { z } from "zod";
import { TasksService } from "./tasks.service";
import {
  createTaskSchema,
  taskIdSchema,
  updateTaskSchema,
} from "./tasks.schemas";
reference/TaskFlow_Business_Logic_Snapshot/apps/api/src/tasks/tasks.schemas.ts
import { z } from "zod";
import {
  TaskPriority,
  TaskStatus,
} from "../generated/prisma/client";

const taskTitleSchema = z.string().trim().min(1).max(200);
const taskDescriptionSchema = z.string().trim().max(2000).nullable();

export const taskIdSchema = z.uuid();

export const createTaskSchema = z.strictObject({
  projectId: z.uuid(),
  title: taskTitleSchema,
  description: taskDescriptionSchema.optional(),
  assigneeId: z.uuid().nullable().optional(),
  priority: z.enum(TaskPriority).optional(),
  dueAt: z.iso.datetime({ offset: true }).nullable().optional(),
});
reference/TaskFlow_Business_Logic_Snapshot/apps/api/src/tasks/tasks.service.ts
import { Injectable } from "@nestjs/common";
import {
  Prisma,
  TaskStatus,
} from "../generated/prisma/client";
import { PrismaService } from "../prisma/prisma.service";
import {
  AssigneeNotWorkspaceMemberException,
  DueDateInPastException,
  ProjectNotFoundException,
  TaskNotFoundException,
  TaskStatusTransitionException,
} from "../common/errors/application.exceptions";
import {
  CreateTaskDto,
  UpdateTaskDto,
} from "./tasks.schemas";

const ALLOWED_STATUS_TRANSITIONS: Record<TaskStatus, readonly TaskStatus[]> = {
  TODO: [TaskStatus.IN_PROGRESS, TaskStatus.CANCELED],
reference/TaskFlow_Business_Logic_Snapshot/tests/business-rules-smoke-tests.ps1
# Phase 14 business-rule smoke checks.
# Replace placeholders with IDs from your development seed/database.

$api = "http://127.0.0.1:4000"
$taskId = "<TASK_UUID>"
$workspaceMemberUserId = "<MEMBER_USER_UUID>"
$nonMemberUserId = "<NON_MEMBER_USER_UUID>"

# Allowed transition: TODO -> IN_PROGRESS
Invoke-RestMethod -Method Patch `
  -Uri "$api/tasks/$taskId" `
  -ContentType "application/json" `
  -Body '{"status":"IN_PROGRESS"}'

# Allowed transition: IN_PROGRESS -> DONE. completedAt should be populated.
Invoke-RestMethod -Method Patch `
  -Uri "$api/tasks/$taskId" `
  -ContentType "application/json" `
  -Body '{"status":"DONE"}'