Osy#betaRun it on Osyrin: osyrin.com

Reference / Project

project layout

app.osy · model/ · seed/ · migrations/ · tests/

The shape of an Osy# project — a manifest at the root, and folders for model, seed, migrations and tests. What you put where decides what gets deployed and what only ever runs.

stableprojectlayout

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 features

A 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#

Related

app.osy

The manifest at the root of every project. It names the app, says which files are model, seed, migrations and tests…

Running tests locally

Runs your app's tests against a Platform on your own machine — no account, no network, no setup beyond a running local…

Compiling your app

Compiles your app's source into the app on the local platform — the inner-loop compile-and-apply. It applies additive…