Share Code Between Layers
A layer may use the classes of the layers it requires, and only those. This page shows the three ways to share code and how to keep osdd:check green with each.
Require the layer
When functional/invoices uses Functional\Orders\Models\Order, it requires functional/orders. For a new layer, pass --requires:
php artisan osdd:layer functional/invoices --requires=functional/orders
For an existing layer, add the requirement to its composer.json, then update it:
"require": {
"lomkit/laravel-osdd": "*",
"functional/orders": "*"
}
composer update functional/invoices
Only the tests use a layer? Put it in require-dev: files under tests/ may use it, the rest of the layer may not.
Generate code that points to another layer
The --model option of osdd:policy, osdd:controller, osdd:observer and osdd:factory keeps a fully qualified class of another layer:
php artisan osdd:policy UserInvoicePolicy --model='Functional\Users\Models\User' --layer=functional/invoices
osdd:listener takes an event of another layer when its name starts with a backslash:
php artisan osdd:listener SendInvoice --event='\Functional\Orders\Events\OrderPlaced' --layer=functional/invoices
Move shared code to its own layer
When several domains need the same code, such as a Money value object or a set of contracts, give it a layer of its own, usually in technical/:
php artisan osdd:layer technical/money --generators=provider
php artisan osdd:class Money --layer=technical/money
Each layer that uses it requires technical/money. Keep such layers small and named after what they hold. A common or shared layer ends up holding everything, and every other layer depends on it.
Communicate through events
A layer can react to another one without the other knowing it. functional/orders dispatches OrderPlaced; functional/invoices requires functional/orders and listens to it:
namespace Functional\Invoices\Listeners;
use Functional\Orders\Events\OrderPlaced;
class SendInvoice
{
public function handle(OrderPlaced $event): void
{
// ...
}
}
The dependency goes one way: functional/orders never references functional/invoices. Turn on event discovery to register the listener without Event::listen().
Break a cycle
osdd:check fails when layers require each other:
ERROR Cycle in the layer requirements: functional/orders -> functional/invoices -> functional/orders.
Find what each side needs from the other. Move it into a third layer both require, or turn one direction into an event as above. Then remove the requirement that closed the cycle.
Allow an exception
Some references are fine without a requirement, such as contracts every layer may use. List them in osdd.boundaries.ignore, see Ignoring a dependency. Keep the list short: an ignored dependency is still a dependency.
See the result
php artisan osdd:graph
graph LR
functional_invoices["functional/invoices"]
functional_orders["functional/orders"]
technical_money["technical/money"]
functional_invoices --> functional_orders
functional_invoices --> technical_money
functional_orders --> technical_money
osdd:graph draws what the layers declare. osdd:check checks that the code matches it.