All notable changes to this project will be documented in this file. This project adheres to Semantic Versioning and this changelog format.
2.0.0 - 2024-12-07
- BREAKING Removed the sub-namespaces for ports provided by this package, i.e.:
Contracts\Application\Ports\Driven
all interfaces are no longer in sub-namespaces; andContracts\Application\Ports\Driven
also has the same change.
- BREAKING Renamed the
InboundEventBus\EventDispatcher
port toInboundEventDispatcher
. - BREAKING Renamed the
OutboundEventBus\EventPublisher
port toOutboundEventPublisher
. - Upgraded to PHPStan v2.
- The result class now has a
Result::fail()
static method to create a failed result. This is an alias of the existingResult::failed()
method. - BREAKING The
Entity
interface (and therefore theAggregate
interface too) now has agetIdOrFail()
method on it. Although technically breaking, if you are using theIsEntity
orIsEntityWithNullableId
traits then this method is already implemented. - New
AggregateRoot
interface so that an aggregate root can be distinguished from a regular aggregate or entity.
- Remove deprecation message in PHP 8.4.
- The
Uuid
identifier class now has agetBytes()
method - Can now get a nil UUID from the
Uuid::nil()
static method.
- Made resolution of inner handlers lazy in all buses. In several the handler was immediately resolved, so that the
handler middleware could be calculated. Buses that support handler middleware now first pipe through the bus
middleware, then resolve the inner handler, then pipe through the handler middleware. This allows inner handler
constructor injected dependencies to be lazily resolved after the bus middleware has executed. This is important when
using the setup and teardown middleware for bootstrapping services that may be injected into the inner handler. Buses
that now lazily resolve inner handlers are:
- Command bus
- Query bus
- Inbound integration event bus
- Outbound integration event bus
- Queue bus
Refer to the Upgrade Guide.
- BREAKING The command bus interface now has a
queue()
method. Our command dispatcher implementation has been updated to accept a queue factory closure as its third constructor argument. This is an optional argument, so this change only breaks your implementation if you have manually implemented the command dispatch interface. - The
FailedResultException
now implementsContextProvider
to get log context from the exception's result object. In Laravel applications, this means the exception context will automatically be logged. - The
Result
interface has a newabort()
method. This throws aFailedResultException
if the result is not successful. - The inbound integration event handler container now accepts an optional factory for a default handler. This can be
used to swallow inbound events that the bounded context does not need to consume. We have also added
a
SwallowInboundEvent
handler that can be used in this scenario.
- BREAKING Package now uses a hexagonal architecture approach, which helps clarify the relationship between the
application and infrastructure layers. This means a number of interfaces have been moved to
the
Contracts\Application\Ports
namespace, with them differentiated between driving and driven ports. - BREAKING As a number of interfaces had to be moved to a
Ports
namespace, we've tidied them all up by removing theInterface
suffix and moving them to aContracts
namespace. - BREAKING We've also removed the
Trait
suffix from traits. To avoid collisions with interfaces, we've useIs
a prefix where it makes sense. For example,EntityTrait
has becomeIsEntity
. - BREAKING The
DomainEventDispatching
namespace has been moved fromInfrastructure
toApplication
. This was needed for the new hexagonal architecture approach, but also makes it a lot clearer that domain events are the way the domain layer communicates with the application layer. - BREAKING The event bus implementation has been split into an inbound event bus (in the application layer) and an outbound event bus (in the infrastructure layer). With the new hexagonal architecture, this changes was required because inbound events are received via a driving port, while outbound events are published via a driven port.
- BREAKING Refactored the queue implementation so that commands are queued. The queue implementation was previously not documented. There is now complete documentation in the Asynchronous Processing chapter Refer to that documentation to upgrade your implementation.
- BREAKING For clarity, the following classes have had their
pipeline
constructor argument renamed tomiddleware
. You will need to update the construction of these classes if you are using named arguments:Application\Bus\CommandDispatcher
Application\Bus\QueryDispatcher
Application\InboundEventBus\EventDispatcher
Application\DomainEventDispatching\Dispatcher
Application\DomainEventDispatching\DeferredDispatcher
Application\DomainEventDispatching\UnitOfWorkAwareDispatcher
- BREAKING The command and query validators have been merged into a single class -
Application\Bus\Validator
. This is because there was no functional difference between the two, so this tidies up the implementation. - BREAKING Renamed the bus
MessageMiddleware
interface toBusMiddleware
interface. Also changed the type-hint for the message fromMessage
toCommand|Query
. This makes this interface clearer about its purpose, as it is intended only for use with commands and queries - i.e. not integration event messages. - BREAKING the
ResultContext
andObjectContext
helper classes have been moved to theToolkit\Loggable
namespace. - BREAKING The
Result::value()
method now throws aFailedResultException
if the result is not successful. Previously it threw aContractException
. - BREAKING The unit of work implementation has been moved to the
Application\UnitOfWork
namespace. Previously it was inInfrastructure\Persistence
. This reflects the fact that the unit of work manager is an application concern. The unit of work interface is now a driven port.
- BREAKING The pipeline builder factory was no longer required, so the following classes/interfaces have been
deleted. Although breaking, this is unlikely to affect your implementation as these classes were only used internal
within our bus and dispatch implementations.
Toolkit\Pipeline\PipelineBuilderFactoryInterface
Toolkit\Pipeline\PipelineBuilderFactory
- BREAKING Removed the following previously deprecated event bus middleware:
LogOutboundIntegrationEvent
- useInfrastructure\OutboundEventBus\Middleware\LogOutboundEvent
instead.LogInboundIntegrationEvent
- useApplication\InboundEventBus\Middleware\LogInboundEvent
instead.
- BREAKING Removed the
Infrastructure::assert()
helper. This was not documented so is unlikely to be breaking.
- New integration event middleware:
NotifyInUnitOfWork
for notifiers that need to be executed in a unit of work. Note that the documentation for Integration Events incorrectly showed theExecuteInUnitOfWork
command middleware being used.SetupBeforeEvent
for doing setup work before an integration event is published or notified, and optionally teardown work after.TeardownAfterEvent
for doing teardown work after an integration event is published or notified.LogInboundEvent
for logging that an integration event is being received.LogOutboundEvent
for logging that an integration event is being published.
- The following integration event middleware are deprecated and will be removed in 2.0:
LogInboundIntegrationEvent
: useLogInboundEvent
instead.LogOutboundIntegrationEvent
: useLogOutboundEvent
instead.
- Allow an outbound integration event handler to implement a
publish()
method. Thehandle()
method is still supported, butpublish()
makes more sense to describe what the handler does with the event it has been given.
- Added missing UUID 7 and 8 methods to the UUID factory interface.
- The
Result::error()
method now correctly returns the first error message even if it is not on the first error in the list.
- BREAKING The following deprecated interfaces have been removed:
Bus\CommandInterface
useToolkit\Messages\CommandInterface
instead.Bus\QueryInterface
useToolkit\Messages\QueryInterface
instead.Bus\DispatchThroughMiddleware
useToolkit\Messages\DispatchThroughMiddleware
instead.Infrastructure\Log\ContextProviderInterface
useToolkit\Loggable\ContextProviderInterface
instead.
- New
FailedResultException
for throwing result objects that have not succeeded.
- BREAKING: The
UnitOfWorkAwareDispatcher
now queues deferred events to be dispatched before the unit of work commits. Previously it queued them for after the commit. This changes allows communication between different domain entities to occur within the unit of work, which is the correct pattern. For example, if an entity or aggregate root needs to be updated as a result of another entity or aggregate dispatching a domain event. It also allows an outbox pattern to be used for the publishing of integration events. This is a breaking change because it changes the order in which events and listeners are executed. Listeners that need to be dispatched after the commit should now implement theDispatchAfterCommit
interface.
- The
ExecuteInUnitOfWork
middleware now correctly prevents the unit of work committing if the inner handler returns a failed result. Previously the unit of work would have committed, which was incorrect for a failed result.
- New event bus notifier implementation that was previously missing. This completes the event bus implementation.
- New message interfaces (command, query, integration event) added to the toolkit.
- New loggable context provider interface added to the toolkit.
- Module basename now supports namespaces where an application only has a single bounded context.
- BREAKING Moved the following interfaces to the
Toolkit\Messages
namespace:MessageInterface
IntegrationEventInterface
- BREAKING Interfaces that type-hinted
Bus\CommandInterface
,Bus\QueryInterface
orBus\MessageInterface
now type-hint the new interfaces in theToolkit\Messages
namespace. - BREAKING Moved the
EventBus
implementation fromInfrastructure\EventBus
toEventBus
. In Deptrac, this namespace is now part of the Application Bus layer. Renamed the publisher handler and publisher handler containers to integration event handler and container - so that they can be used for both the publisher and notifier implementations. - BREAKING Removed the
EventBus\PublishThroughMiddleware
interface. Use theToolkit\Messages\DispatchThroughMiddleware
interface instead.
- BREAKING removed the
deptrac-layers.yaml
file, in favour of applications including the classes in their own Deptrac configuration.
- The
Bus\CommandInterface
,Bus\QueryInterface
andBus\DispatchThroughMiddleware
interfaces have been deprecated in favour of the new interfaces in theToolkit\Messages
namespace. - The
Infrastructure\Log\ContextProviderInterface
is deprecated in favour of the newToolkit\Loggable\ContextProviderInterface
interface.
- Removed
final
from theDeferredDispatcher
andUnitOfWorkAwareDispatcher
classes so that they can be extended.
- New
DeferredDispatcher
class for dispatching domain events when not using a unit of work. - New UUID factory interface and class, that wraps the
ramsey/uuid
factory to return UUID identifiers. - GUIDs that wrap UUIDs can now be created via the static
Guid::fromUuid()
method. - New
SetupBeforeDispatch
andTearDownAfterDispatch
bus middleware, that can be used either to set up (and optionally tear down) application state around the dispatching of a message, or to just do tear down work. - The
EventBus
namespace now has a working implementation for publishing integration events. - Can now provide a closure to the
ListOfErrorsInterface::first()
method to find the first matching error. - Added the following methods to the
ListOfErrorsInterface
:contains()
- determines whether the list contains a matching error.codes()
- returns an array containing the unique error codes in the list.
- Added an
ErrorInterface::is()
method to determine whether an error matches a given code.
- BREAKING - renamed the domain event
Dispatcher
class toUnitOfWorkAwareDispatcher
. - BREAKING - removed the
IntegrationEvents
namespace and moved to theInfrastructure\EventBus
namespace. - BREAKING - the
IntegrationEventInterface
now expects the UUID to be an identifier UUID, not a Ramsey UUID. - The UUID factory from the
ramsey/uuid
package is now used when creating new UUID identifiers.
- The unit of work manager now correctly handles re-attempts so that deferred events are not dispatched multiple times.
- New
LazyListOfIdentifiers
class for lazy iteration over a list of identifiers. - Log context for a result now includes the value if it is a scalar value (string, integer, float, or boolean).
- BREAKING: add the
$stack
property to theListTrait
andKeyedSetTrait
traits, and use generics to indicate the value they hold. This is breaking because it will cause PHPStan to fail for existing classes the use these traits. - BREAKING: renamed the
LazyIteratorTrait
toLazyListTrait
and defined its values using generics.
- Log context for a result now includes the value if it implements
ContextProviderInterface
orIdentifierInterface
. - BREAKING: added a
safe
method to theResultInterface
, that gives access to the result value without throwing an exception if the result is an error.
- Remove
EntityTrait::getId()
nullable return type as it is always set. - Fix generic return type on
Result::ok()
method.
- BREAKING: moved the
Bus\Results
namespace toToolkit\Result
. As part of this move, the interfaces and classes in this namespace no longer implement the logContextProviderInterface
, as this is an infrastructure dependency. Instead, the newInfrastructure\Log\ObjectContext
andInfrastructure\Log\ResultContext
class can be used to create context for either a result or an object. - All constructor arguments for the
Toolkit\Result\Error
object are now optional. This allows named arguments to be used when creating an error object. - The
Toolkit\Result\Error
object can now accept only a code, previously it had to have a message. - The following interfaces no longer extend the log
ContextProviderInterface
. Instead, classes only need to implement that log interface if they need to customise how that class is logged.Bus\MessageInterface
Infrastructure\Queue\QueueableInterface
- The command and query validators now allow rules to return
null
to indicate no errors. - The following dispatchers now accept pipeline builder factories or pipeline containers into their constructor. This
simplifies creating them, as in most cases a pipeline container can be provided from a dependency helper.
CommandDispatcher
QueryDispatcher
DomainEventDispatching\Dispatcher
Queue
- BREAKING: changed the
ErrorIterableInterface
toListOfErrorsInterface
. Result objects now only accept list of errors. TheKeyedSetOfErrors
class can be used to convert a list into a keyed set if needed. This helps simplify the error handling logic, which was overly complex by having a generic error iterable interface that could either be a list or a keyed set. - BREAKING: The error interface no longer extends
Stringable
. Use$error->message()
instead, or compose a string from multiple properties of the error object. - BREAKING: The code on an error object is now type-hinted as a
BackedEnum
ornull
- previously it wasmixed
. Error codes should be from a defined list, therefore an enum is the correctly defined type. - BREAKING: The
PipelineBuilderFactory::cast()
method has been renamedmake()
.
- BREAKING: removed the
IterableInterface
as there is no need for a list and a keyed set to inherit from the same interface.
Initial release.