Whetstone.
All courses
dddIntermediate

Domain-driven design, the Fusion way

A crash course in domain-driven design that starts from zero and takes you to fluent. Part one teaches the concepts through the lens you already have (NestJS, NextJS, TypeScript): entities versus value objects, aggregates that own their invariants, the anaemic-domain trap, and errors as values. Part two applies them to the real Fusion backend and the exact conventions its PRs are reviewed against. Part three walks the real review comments you received (from Mustafa, Sergii, Nick, and Nisha) and decodes what each one meant. You leave able to design it right up front, and able to read your own reviews.

Start the course 26 lessons 317 min
DDCrash course
Curriculum

26 lessons across 9 modules

01DDD from zero, in a language you know6 lessons · 73 min
02Errors as values, the Fusion mechanics3 lessons · 37 min
03Value objects2 lessons · 25 min
04Entities and the aggregate root2 lessons · 24 min
05The layers2 lessons · 23 min
06Application orchestration and CQRS2 lessons · 25 min
07Persistence as dumb IO2 lessons · 23 min
08Decode your own reviews6 lessons · 75 min
09Reading a review like the team1 lessons · 12 min
Capstone

Refactor a NestJS-style blob into the Fusion idiom

Here is code a Nest/Next developer would write on instinct, and that Fusion review would send straight back:

csharp
public class NoteService
{
    private readonly FusionDbContext _db;
    public NoteService(FusionDbContext db) => _db = db;

    public async Task<FlightNote> SaveNote(string flightKey, string body)
    {
        if (string.IsNullOrWhiteSpace(flightKey))
            throw new ArgumentException("flight key required");
        if (body.Length > 4000)
            throw new ArgumentException("too long");
        var row = new FlightNote { EjdpUniqueFlightKey = flightKey, Body = body };
        _db.FlightNotes.Add(row);
        await _db.SaveChangesAsync();
        return row;
    }
}

Refactor it into the Fusion shape from memory:

  1. A FlightNoteVo value object: private constructor, public static Result<FlightNoteVo> Create(string? key, string? body) that trims and validates, accumulating both failures. No exceptions.
  2. A SaveFlightNote use case: a sealed class, a ctor-injected IFlightNoteRepository port, an Execute(request, ct) returning Task<Result<...>>. It builds the VO, and on success taps the repository.
  3. An IFlightNoteRepository port (application layer) with a Save/SaveMany method, implemented by a persistence adapter that maps the VO to a DBO and calls SaveChangesAsync — and never calls Create.
  4. Rename it: there is no NoteService in Fusion. The use case is verb-first (SaveFlightNote); no Service/Manager/Provider.

Then run it past the anti-pattern checklist yourself: exceptions became Result? invalid VO unconstructable? logic out of the use case? layers pointing inward? When you can do that whole refactor without looking, the review stops calling it slop.

This is the same blob you’ll meet in Module 1, Lesson 1 as “what slop is.” By the time you reach it here you’ll have the vocabulary to name every problem and the instinct to have designed it right the first time.