Clean architecture

Domain

The note entity and the repository port, with no Flutter import.

clean-architectureflutter

In this track the entity lives at lib/domain/notes/. The shared failure type lives at lib/domain/shared/. A second feature would add domain/notebooks/, not a new top-level folder.

A note has an identity, a title, and a body. Note.create is the constructor that checks the title. It trims the title and the body. A blank title throws ValidationFailure('Title is required') before any repository is involved. The row mapper rebuilds with the field constructor. The title rule has already run on create.

dart
factory Note.create({
  required String id,
  required String title,
  required String body,
  required DateTime createdAt,
}) {
  final trimmed = title.trim();
  if (trimmed.isEmpty) {
    throw const ValidationFailure('Title is required');
  }
  return Note(
    id: id,
    title: trimmed,
    body: body.trim(),
    createdAt: createdAt,
  );
}

NoteRepository is the port: save and findAll. It returns notes, not rows. The files are lib/domain/notes/note.dart and lib/domain/notes/note_repository.dart.

Nothing in the domain folder imports Flutter, Riverpod, Beamer, or the data implementation. That is the dependency rule for this track, same as the others.

Next: Application.