Summary#
An Osy# project is a manifest and four folders. The folders are not a style preference — they decide what is deployed and what merely runs, so putting a file in the wrong one has consequences.
Signature#
app.osy the manifest — what compiles, what is tested, which capabilities the app uses
model/ entities, functions, security, UI — one folder per feature (compiled and deployed)
seed/ data the app needs in order to exist (compiled and deployed)
migrations/ explicit schema and data migrations (passed to a deploy, not compiled)
tests/ [Test] functions (run, never deployed)Description#
Why the split matters#
model/ and tests/ are the two you will use every day, and the line between them is the one that matters: tests
are never deployed. A [Test] function lives in tests/, runs against a throwaway clone of the app, and never
reaches production — so a test may create rows, break rules and assert on the wreckage without any of it mattering.
Anything in model/ is the application. If you put a test helper there, you have shipped it.
Splitting model/ up — by feature#
model/ is a glob, so the compiler reads every file under it as one unit and no folder changes what compiles. The
shape is yours — and the one every project here uses, and osy init writes, is one folder per feature:
model/
orders/ a feature: everything about it, together
order.osy its entities and their security
fulfilment.osy its lifecycle and functions
orders.osy its pages — one file per page
order-detail.osy
catalogue/
product.osy
catalogue.osy
people/ who signs in: the roles, the policies named for them, the login page
people.osy
look/ not a feature: the theme, the frame around every page, art
theme.osy
frame.osy
tests/
orders.test.osy one test file per feature, named for its folder
catalogue.test.osy
security.test.osy a cross-cutting file is fine where a rule spans featuresA feature's folder is the whole of what to read to change it: the entity, the rule on who may write it, the function
that moves it along, and the page that shows it sit side by side, so a change to "orders" never sends you looking
through a pages/ folder for the screen that goes with it. A page that serves several features — a dashboard, a home
page over everything — goes in the folder of the feature it is mostly about, or in one of its own (model/home/).
Each page is still its own file. A page is what you revise most, and one in its own file is edited without re-reading the entities and three other screens to find it.
osy lint says so (CONSIDER, ui-page-outside-its-feature-folder) when a page uses only one feature folder's
entities and functions (counting what its own components use, the ones it draws) but lives somewhere else, and spells the git mv that moves it. It is silent on a page that
names two features, on one that names nothing of the project's, and on a project that does not use feature folders
at all. Moving a file never changes what compiles, so the move is safe: osy validate answers the same before and
after.
There is no required file naming and no ordering rule — a function in one folder may freely reference an entity declared in another.
Seed vs migrations#
They are easy to confuse and they answer different questions.
seed/— data the app cannot exist without: the roles, the statuses, the country list. It is re-applied to make the app be what it says it is, so it must be safe to run repeatedly.migrations/— a one-time, explicit change to an existing deployment: a column that needs backfilling, a non-additive schema change you have reviewed and authorised, or where a workflow run parked in a state you changed now stands.
If you find yourself wanting to "just seed" a production fix, you want a migration.
A *.migration is not compiled with your model, and it could not be: it is written against one PAIR of
versions and applied once, so re-applying it on every compile is exactly what must not happen. You hand it to the
deploy that needs it — osy compile --migration migrations/<name>.migration — and
osy compile --generate-migration writes it here for you to review first.
See also#
- app.osy — the
app.osythat names these folders - Compiling your app —
osy compile, which compiles model/ and seed/, and takes a migration with--migration - Running tests locally — running what is in
tests/