Skip to main content
๐Ÿ“œ WAYPOINT LESSON

The Service Layer: Validation, Authorization, Persistence

โญโญ intermediateโณ 26 min read๐Ÿ“ Lesson 154 of 265

The capstone's outer ring: every operation checked (is it valid? is it allowed?), state flowing through the repository, snapshots as JSON โ€” and async at the edges.

Operations and their gates

Each service operation answers three questions, in order:

  1. Valid? โ€” input shape (lesson 1). Bad input โ†’ ArgumentException.
  2. Allowed? โ€” authorization. Only an Admin adds users to the system; only the project's OwnerId (or an Admin) creates tasks in it or reassigns them. Not allowed โ†’ InvalidOperationException (a state problem: this caller, this state). Bad actor knowledge is a 401/403 in a real API; here the exception type carries the same boundary.
  3. Possible? โ€” does the referenced project/task exist? Missing โ†’ KeyNotFoundException or a TryXxx return, per the operation's contract.

Repositories behind interfaces

The engine's services depend on IUserStore/IProjectStore/ITaskStore interfaces; an in-memory implementation backs tests, a JSON-file implementation backs snapshots. Same code, swappable edge โ€” the module 11/15 /19 lesson composed into one system:

public interface ITaskStore
{
    Task Add(Task t);                 // Task = System.Threading.Tasks.Task
    Task<Task?> Find(int id);
    Task Update(Task t);
    Task<System.Collections.Generic.IReadOnlyList<Task>> All();
}

Reporting is just LINQ

Overdue, workload per assignee, project progress โ€” all one-pass pipelines over All():

var open = tasks.Where(t => t.Status != Status.Done)
                .GroupBy(t => t.AssigneeId)
                .OrderByDescending(g => g.Count());

Persistence as a snapshot

JSON round-trip: on demand, serialize the stores (enum values as strings for readability) to a file; on boot, load. The rules from module 14 apply โ€” case-insensitive reads, string enums, and never trusting a file's shape.

Async at the edges

Store I/O is async (a real DB would be); the service composes with await, propagates CancellationToken, and the UI layer never blocks on .Result โ€” the module 9 contract, one last time.

โšก Now practice

Ready to Code
Capstone Milestones: Domain to EngineBuild the engine in milestones: task domain with validation, a task repository behind its interface, the authorized service layer, reporting pipelines, and the async JSON snapshot.
4 challenges ยท ยท ~42 min