Bounded is a framework that supports the Domain Driven Design approach. It enables you to leverage Scala and Akka technologies in a way that gives you a structured DDD based application.
class Cargo(cargoId: AggregateRootId)
extends AggregateRootActor
with AggregateStateCreator
with ActorLogging {
override def aggregateId:
AggregateRootId = cargoId
override def handleCommand(
command: DomainCommand,
currentState: AggregateState)
: Reply = {
command match {
case cmd: PlanCargo =>
Ok(Seq(
CargoPlanned(cmd.metaData,
cmd.cargoId,
cmd.trackingId,
cmd.routeSpecification)))
case cmd: SpecifyNewRoute =>
Ok(Seq(
NewRouteSpecified(
cmd.metaData,
cmd.cargoId,
cmd.routeSpecification)))
case other =>
Ko(UnexpectedCommand(other))
}
}
}
The example shows the Cargo example as described in the book of Eric Evans. Bounded takes care of the persistence of the aggregate root via an event sourced mechanism based on akka persistence. Next to that, it takes care of handling the internal state of the aggregate root.
case class CargoAggregateState(
trackingId: TrackingId,
routeSpecification: RouteSpecification)
extends AggregateState {
override def update(evt: DomainEvent)
: AggregateState = {
evt match {
case CargoPlanned(meta,
cargoId,
trackingId,
routeSpecification) =>
CargoAggregateState(trackingId,
routeSpecification)
case NewRouteSpecified(meta,
cargoId,
routeSpecification) =>
this.copy(routeSpecification
= routeSpecification)
case other => this
}
}
}
Bounded makes use of a Event Sourcing approach. All logic is expressed in events that allows you to extend your application based on those events. The resumable projections are built with the reactive technology akka streams. Using an event store also means that you have the full history of the application at hand, allowing to create new historic based views later in time.
The bounded framework enables a CQRS style Domain Driven Design application.
┌─────────┐ ┌──────────────────────┐
│ │ │ DOMAIN │
┌───────┐ │ Command │ │ ┌───────┐ │
│ │ │ Routing │ │ │ │ │
│ │────────▶ ├───┼────────▶ │─────┼────────────────┐
│ │ │ │ │ │ AR │ │ │
│GRAPHQL│ │ │ │ └───────┘ │ ▼
│ API │ │ │ │ │ ┌────────────────┐
│ │ └─────────┘ │ │ │ PERSISTENCE │
│ │ │ │ │ │
│ │ │ │ │ │
│ │◀─────────┐ └──────────────────────┘ │ EVENT │
│ │ │ │ STORE │
│ │ │ │ │
│ │ │ ┌──────────────────────┐ │ │
└───────┘ │ │ Materializers │ │ │
│ │┌─────────┬─────────┐ │ │ │
│ ││ │ | │ └────────────────┘
│ ││ Item │ Item │ │ │
└───────────┤│ Query │ Writer │◀┼────────────────┘
││Interface│ │ │
│└─────────┴─────────┘ │
│ │
│ │
│ │
└──────────────────────┘
AR = Aggregate Root
The framework allows you to write a CQRS based application that seperates the intent by issueing commands to your application and thereafter make use of the data via projection created query stores. Resumable projections allow to create any kind of query store that is applicable for your application. The projections listen to the events created and therafter create their own records in a store that may be a relational database store, elastic search, a cache or any kind of other storage. The tracking of the projections ensures that no events are forgotten even when the application is upgrading. When your projections need updates, it is always possible to replay the full store.
The code is Apache2 licensed and available in the Cafienne Bounded Framework Github Repository .
Are found at https://github.com/cafienne/bounded-framework/releases