Weave Code
Weaver
Helps Laravel developers discover, compare, and choose open-source packages. See popularity, security, maintainers, and scores at a glance to make better decisions.
Feedback
Share your thoughts, report bugs, or suggest improvements.
Subject
Message

Lendo Core Domain Laravel Package

lendo-stp-gdansk/lendo-core-domain

Core domain layer for Lendo STP Gdańsk, providing shared domain models, value objects, enums, and contracts for Laravel/PHP applications. Encapsulates reusable business rules and building blocks to keep services consistent.

View on GitHub
Deep Wiki
Context7

Getting Started

1. Require and inspect before you code

composer require lendo-stp-gdansk/lendo-core-domain

This package has zero stars and zero dependents, so treat the first ten minutes as due diligence rather than setup. Open the vendor directory and check three things before writing a line of application code:

  • vendor/lendo-stp-gdansk/lendo-core-domain/composer.json — PHP version, Laravel/Illuminate constraints, and whether it pulls in a message bus, serializer, or DB abstraction you did not expect.
  • src/ layout — look for the event engine (aggregate roots, event store, repository) and the read model engine (projectors, read repositories) as separate namespaces. That split tells you where your code belongs.
  • config/ and database/migrations/ — if the package ships an event store table or projection checkpoint table, you need those migrations before anything works.

2. Register the provider

If the package declares extra.laravel.providers, discovery handles it. If not, add it to bootstrap/providers.php (Laravel 11+) or config/app.php. Then publish anything publishable:

php artisan vendor:publish --provider="Lendo\CoreDomain\CoreDomainServiceProvider"

Verify the config keys for the event store connection and the read model connection. These are frequently separate databases, and pointing both at default is a common first-run mistake.

3. First use case: one aggregate, one event, one projection

Don't start with your hardest domain. Pick a single write operation — e.g. RegisterCustomer — and trace it end to end:

  1. An aggregate method that records an event.
  2. Persist the aggregate through the package's repository/event store.
  3. A projector that consumes the event and writes a read model row.
  4. A query that reads only from the read model.

Once that loop closes, you understand the package's contract. Everything else is repetition.

Implementation Patterns

Aggregate + event store as the write side

The event engine is almost certainly built around an aggregate root base class that records domain events and a repository that loads by replaying them. Your domain objects should extend whatever base the package provides and stay free of Eloquent. Keep constructors private or named-constructor-only; the engine needs a way to reconstitute an aggregate from history that bypasses your normal validation. Check src/ for the reconstitution factory or apply() method early — it determines how strict you can make your invariants.

Thin commands, fat aggregates

Resist the Laravel habit of putting business logic in a controller or a service. Pattern:

final class RegisterCustomerHandler
{
    public function __construct(private CustomerRepository $customers) {}

    public function __invoke(RegisterCustomer $command): void
    {
        $customer = Customer::register($command->id, $command->email);
        $this->customers->save($customer);
    }
}

The win of event sourcing disappears the moment an aggregate is mutated from outside itself.

Projectors as the read side — one projection per query screen

Treat the read model engine as a set of independent, idempotent consumers. Each projection should be able to answer: what does it consume, what table does it own, and what is its checkpoint key? Keep projections small and screen-shaped rather than mirroring the write model — a projection that reproduces the aggregate schema has no reason to exist.

Dispatch after commit

If the package dispatches events synchronously on save, a projector failure rolls back your write. Wrap dispatch in DB::afterCommit() or configure a queue connection in the package config so projections run asynchronously. Decide this deliberately on day one; retrofitting async dispatch after you have data is painful.

Rebuilds as a normal operation

Ask immediately: how do I replay all events into a fresh read model? Find the rebuild command (or write one that drops the projection table and resets its checkpoint). If the package has no rebuild path, that's a blocker — you cannot fix a projection bug in production without it.

Testing pattern

Use an in-memory or SQLite event store in tests and assert on recorded events rather than on database rows:

$customer = Customer::register($id, '[email protected]');
$this->assertContains(EmailChanged::class, array_map('get_class', $customer->releaseEvents()));

For projections, feed the projector hand-built event instances. That keeps read-side tests fast and free of the write side entirely.

Gotchas and Tips

  • Zero dependents means zero field reports. Pin the exact version in composer.json ("lendo-stp-gdansk/lendo-core-domain": "1.x-dev#<sha>" or a tagged release) so a silent upstream change can't break production. Consider forking into your VCS if this is load-bearing.
  • Event schema migration is the real tax. Events are permanent. Adding a required field to an event class breaks replay of every historical event. Look for an upcasting/versioning mechanism in the engine; if there isn't one, plan for it yourself — keep events as flat, additive-only DTOs with nullable new fields, or version them by class name.
  • Serialization of value objects. Custom value objects (Money, Email, IDs) will only survive a round trip if the package's serializer knows them. Test a save → load → assert-equal cycle early. If the serializer is json_encode-based, implement a contract on your VOs rather than relying on __toString.
  • Projection idempotency. Async consumers will re-deliver. Every projector's handle must be safe to run twice — upsert, not insert; guard on event ID if the package doesn't dedupe for you.
  • Separate DB connections. Read model queries must use the read connection, not the write one. If the package resolves connections from config, confirm there's no config('database.default') fallback hiding in the projector base class.
  • Migrations order. The package's event store migration and your projection migrations must run in the right order for a clean migrate:fresh. Check whether the package loads migrations automatically (loadMigrationsFrom) or whether you must publish them — mixing both causes duplicate-table errors.
  • Debugging slow write paths. Event-sourced saves grow linearly with aggregate lifetime. If a single aggregate accumulates hundreds of events, snapshotting is required. Search the engine for a snapshot repository; if absent, keep aggregates short-lived by design.
  • Extension points worth knowing before you need them: the event store implementation (swappable for a different backing store), the serializer, the checkpoint store for projections, and the dispatcher. Bind your own implementations in a service provider rather than editing vendor code.
  • Read the tests in the package. With no community usage, the vendor's own test suite is your best documentation of intended behaviour — and often the only place the reconstitution or replay API is actually demonstrated.
Weaver

How can I help you explore Laravel packages today?

Conversation history is not saved when not logged in.
Prompt
Add packages to context
No packages found.
elsayed85/laravel-api-exceptions
ouhssini/laravel-demo-mode
daworks/ncloud-outbound-mailer-driver
gp247/cms
ignibyte/aic-gates
nexa-merchant/blog
novasa/laravel-language-center
opencage/geocode
adolfoholzer/parameters
livenetworks/ln-starter
shitoudev/permissionmanager
johannesclimacus/laravel-tui
hocvt/seotools
vitalybaev/sypexgeo
amphibee/themosis-ignition
gito-me/fast-excel
frankyso/laravel-ipaymu
geolize-studio/laravel-chile
boomerangui/chat
cgv/localized-resource