rmb32 / barnark
PHP architecture management — scaffold a codebase from an architecture template, check it conforms, and switch it to another template.
Requires
- php: ^8.5
- ext-ds: ^2.0
- nikic/php-parser: ^5.0
- php-ds/php-ds: ^2.0
- rmb32/barncept: ^1.0
- rmb32/barnscaff: ^1.0
- symfony/console: ^7.0
- symfony/filesystem: ^7.0
- symfony/finder: ^7.0
- symfony/process: ^7.0
Requires (Dev)
- deptrac/deptrac: ^4.7
- infection/infection: ^0.35.4
- phpstan/phpstan: ^2.2
- phpunit/phpunit: ^11.0
- rector/rector: ^2.6
- squizlabs/php_codesniffer: ^4.0
Suggests
None
Provides
None
Conflicts
None
Replaces
None
This package is auto-updated.
Last update: 2026-10-07 18:34:05 UTC
README
Part of BarnSuite.
Architecture management for PHP projects. Barnark keeps a catalogue of architecture templates — Domain Driven Design, Feature Based Architecture, Model View Controller, Library, Package App — and lets you scaffold a project from one, check a project against one, and switch a project from one to another. It isn't tied to any single architectural style.
Barnark is a thin layer over Barncept (concept graphs) and Barnscaff (filesystem projection). This package is the core library; the command line and browser UI are separate packages.
Requirements
- PHP 8.5+
- Composer
Installation
composer require --dev rmb32/barnark-cli
./vendor/bin/barnark --version
The command line brings this library in with it. docs/installation.md covers a global install and the files Barnark keeps in your project.
Quick start
barnark template:list # what's available
barnark template:scaffold "Library" ./my-package # blank structure from a template
barnark template:check "Library" ./my-package # does the folder match?
barnark template:check # check every folder the project tracks
barnark template:roles ./src # which role does each folder inside play?
barnark template:roles ./src --apply # record them, so checks can see them
barnark template:offers src/Context # what the template lets you create here
barnark template:new Context Billing src/Context # create one, with the structure it carries
barnark template:new RepositoryInterface InvoiceRepository src/Context/Billing/Domain/Invoice
# move a tracked folder to another template, one decision at a time
barnark template:switch Library src
barnark template:switch Library src --apply
A path to your own template schema JSON works anywhere a template name does.
| Command | Description |
|---|---|
barnark template:list | List the bundled architecture templates |
barnark template:scaffold | Build a blank directory structure from a template |
barnark template:check | Check tracked folders, or a given directory, for consistency against a template |
barnark template:track / template:untrack | Record / forget which template a folder has applied |
barnark template:roles | Work out which template role each folder inside a tracked folder plays, and forget roles whose path is gone |
barnark template:offers | List what the template lets you create inside a folder |
barnark template:new | Create one named instance of a template concept, recorded so checks see it |
barnark template:exclude | Leave a path out of its folder's checks and switches |
barnark template:switch | Plan, step by step, and then run a tracked folder's move to another template |
Run barnark <command> --help for every option.
Switching templates
Switching architecture is never fully automatic, so template:switch asks.
For each piece of real content beyond the old template's skeleton it offers to
move it under one of the new template's concepts (keeping its name or giving it
a new one), leave it where it is, or decide later — and offers to do the same
for the others of its kind. It asks which role a moved folder plays only where
the new concept offers more than one.
$ barnark template:switch Library src
Switching /src from 'Domain Driven Design' to 'Library'
Root namespace [Acme\Shop]
Context/Billing (a Context) — 1 of 2
[1] Move to Feature as Billing (suggested by the mapping)
[2] Move to Feature with a new name
[3] Move somewhere else
[4] Leave it where it is
[5] Decide later
[q] Save and stop
> 1
Do the same for the other 1 Context (Shop)? [Y/n]
These old folders have no place in 'Library':
Kernel/ (and 3 folder(s) inside), Transport/ (and 9 folder(s) inside), Context/
Remove them? Each is only removed if it is still empty. [Y/n]
Plan saved to .barnark/switch-plans/src.json — nothing left to decide.
Apply now? [y/N]
The plan is saved after every answer in .barnark/switch-plans/ (commit it with
the rest of the workspace), so you can stop and run the same command again to
carry on. Nothing on disk changes until the plan is complete and you say so.
barnark template:switch Library src --show # what is decided and what is left
barnark template:switch Library src --apply # run a complete plan without asking
barnark template:switch Library src --discard # throw the plan away
Running the plan moves each unit whole and rewrites its namespace, removes the
old template's folders if you asked (only while they are empty), records the
folder's new template and roles, and excludes whatever you left where it was.
What is inside a moved folder is not restructured: the check that runs straight
after lists it as follow-up. Suggestions come from a bundled mapping for the
pair of templates where one exists, or from --mapping=<path>. The folder must
be tracked, since its current template is where the switch starts. The root
namespace defaults to company / project in a barnark.json in the folder,
or pass --namespace.
Tracked folders
Barnark records which folders have which template applied in
barnark-workspace.json at the project root. template:scaffold adds the folder
(--no-track opts out), template:switch re-points it once it runs and
template:track registers an existing one — so barnark template:check needs no
template or folder, and checks them all.
Checking a real project
A template describes a shape, not your code: Context and Aggregate are roles
your own folders play, not folders the template names. So a tracked folder also
records, per path, which role the thing there plays — and template:check reads
those, so your real contexts and aggregates are checked where they actually are.
template:roles works most of this out for you, because the template already
says what kind of child each concept takes:
barnark template:roles ./src # what it would record
barnark template:roles ./src --apply # record it
Anything it cannot settle is listed as unresolved and left alone — that list is
usually the set of folders your template has no concept for yet. A role recorded
for a file or folder that has since been deleted or renamed is listed too, and
--apply forgets it.
A tracked folder inside another tracked folder answers to its own template: the outer folder's check leaves it alone.
Creating by the template
template:new creates what the template describes. A folder concept arrives
with its fixed structure, and a file concept arrives as a real PHP declaration
in the namespace your composer.json's PSR-4 map gives that path:
<?php
declare(strict_types=1);
namespace App\Context\Billing\Domain\Invoice;
interface InvoiceRepository
{
}
What a file holds is the template's call, set in the role's metadata:
"declares": "interface" or "exception" (a final class otherwise), and
"extension": "html.twig" for files that aren't PHP at all. Your own template
schema can use both. For example, a copy of Model View Controller whose
View role has {"extension": "html.twig"} creates Twig views.
Files need no roles of their own: a folder whose concept holds files accepts any
.php file in it. For content that has no architectural meaning at all, leave it
out altogether. template:switch leaves an excluded path where it is:
barnark template:exclude src/DataFixtures
barnark template:exclude src/DataFixtures --remove
Workspaces
To manage many template-tracked folders together, use
Barnark GUI. A workspace is a directory of folders, each
with a small barnark.json ({"template": "<name>"}), registered in the
workspace's barnark-workspace.json.
From PHP
use Rmb32\Barnark\Internal\Kernel\BarnarkApiFactory;
use Rmb32\Barnark\Api\Application\Template\Query\ListTemplatesQuery;
$api = new BarnarkApiFactory()->create();
$templates = $api->queryBus->dispatch(new ListTemplatesQuery());
Pass a workspace path to create() to enable the workspace commands and queries.
A whole switch, as the CLI and the GUI run it — open a plan, decide it on the plan itself, save it, run it:
use Rmb32\Barnark\Api\Factory\SwitchWorkflowFactory;
use Rmb32\Barnark\Api\ValueObject\LeftoverChoice;
$workflow = new SwitchWorkflowFactory()->create('/path/to/project');
$plan = $workflow->open('/src', 'Library'); // or carry on with the saved plan
$billing = $plan->unit('Context/Billing')->moveTo('Feature');
$plan = $plan->withUnit($billing)->decideAlike($billing); // the other Contexts too
$workflow->save($plan->withRootNamespace('Acme\\Shop')->withLeftoverChoice(LeftoverChoice::Remove));
$result = $workflow->apply('/src'); // records the new template, roles and exclusions
Related packages
- Barnark CLI — the
barnarkcommand. - Barnark GUI — browse workspaces, run checks, and plan and run template switches in a browser.
- Barncept — the schemas the templates are written in.
- Barnscaff — scaffolding and conformance scanning.
More docs
Installation · Internals · Application architecture · History · Known issues
License
Proprietary. See LICENSE. Copyright (c) Roger Barnfather.