Search by

rmb32 / barnark

rogerbarnfather

PHP architecture management — scaffold a codebase from an architecture template, check it conforms, and switch it to another template.

v2.0.0 2026-10-07 12:39 UTC

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.

CommandDescription
barnark template:listList the bundled architecture templates
barnark template:scaffoldBuild a blank directory structure from a template
barnark template:checkCheck tracked folders, or a given directory, for consistency against a template
barnark template:track / template:untrackRecord / forget which template a folder has applied
barnark template:rolesWork out which template role each folder inside a tracked folder plays, and forget roles whose path is gone
barnark template:offersList what the template lets you create inside a folder
barnark template:newCreate one named instance of a template concept, recorded so checks see it
barnark template:excludeLeave a path out of its folder's checks and switches
barnark template:switchPlan, 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 barnark command.
  • 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.