Start with the domain
iMaistro’s central model is a quantity, not an untyped number. Units and dimensions belong in the calculation domain so every screen uses the same rules.
A helper definition describes a repeatable calculation: fields, allowed ranges, output values, explanatory steps, and assumptions. The Flutter presentation layer renders these concepts without becoming the owner of the formulas.
The main boundaries are:
| Boundary | Owns | Does not own |
|---|---|---|
| Calculation domain | Quantities, conversions, expressions, helper formulas, validation | Widgets, navigation, SQLite, network calls |
| Application layer | User actions, selected tools, saving and loading workflows | The physical meaning of a unit |
| Persistence adapters | SQLite schema, queries, transactions, backup serialization | Screen layout or calculation formulas |
| Presentation | Widgets, surfaces, accessibility, focus, diagrams, motion | A second copy of the mobile calculation engine |
This is a small domain-driven design. Boundaries exist where they protect a real rule or dependency. There is no need for an interface around every class.
From web UI to Flutter
A Flutter widget describes a piece of interface. State changes cause the relevant widgets to rebuild. This will feel familiar if you have used a declarative web framework, but layout uses Flutter’s constraint system instead of CSS.
The app has a bounded screen and an on-screen keyboard competing for space. Safe areas, focus, text scaling, and bottom surfaces are part of the layout problem. A phone is also more likely to interrupt the app, rotate, suspend it, or recreate its process than a typical desktop browsing session.
Keep durable state outside a widget’s temporary state. A field currently being edited can live in the presentation layer; a saved project belongs in local storage.
Offline by construction
The included calculator catalog and formulas ship with the app. SQLite keeps settings, history, and saved work on the device. There is no repository method that secretly requires a server to produce a basic result.
“Offline first” here is intentionally simple: this release is local-only. Conflict resolution and synchronization queues are unnecessary until a future feature introduces actual remote data.
The monorepo
The root workspace coordinates three applications: the Flutter mobile app, the Astro marketing/documentation site, and the small Astro download site. The pure Dart calculation package and shared design assets live beside them.
Moon orchestrates commands across these projects. Mise pins tools. Dart and Node retain their native package managers and lockfiles. This keeps dependency resolution understandable without forcing both ecosystems into one package format.
The website demo
The slab example on the marketing page is a deliberately small TypeScript demonstration with its own tests. It is not a JavaScript port of the entire Dart domain. Its purpose is to let a visitor try the units-and-explanation interaction without installing the mobile app.
Adding a new mobile helper does not require adding another website calculator.