Domain
The note entity and the repository port, with no Flutter import.
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.
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.