Open yonahforst opened 7 years ago
No worries. I am glad to answer any questions. I will do my best at explaining the structure of the domain in the example. Just let me know if you have any additional questions that I did not answer.
example
βββ domain
βΒ Β βββ aggregates - the write-models for your domain (command handlers)
βΒ Β βΒ Β βββ idea.js - the bounded context for an idea
βΒ Β βΒ Β βββ project.js - the bounded context for a project
βΒ Β βΒ Β βββ user.js - the bounded context for a user
βΒ Β βββ projections - the read-models for your domain (query handlers)
βΒ Β βββ users.js - a projection that joins the idea, project, and user models
βββ external - domain-agnostic data storage mechanisms
βββ aggregate-stores
βΒ Β βββ memory.js - stores aggregate snapshots in an in-memory database
βββ event-stores
βΒ Β βββ memory.js - stores events in an in-memory database
βββ projection-stores
βββ memory.js - stores projection snapshots in an in-memory database
This example combines concepts from DDD and CQRS. The way that the example is structured assumes that each aggregate has it's own external event store and aggregate store implementation. There are 3 aggregates (users, projects, and ideas), each representing a bounded context as well as a consistency boundary. The aggregates are only responsible for processing a command and either returning new events or throwing an error. A single command may create multiple events, but those events may not span multiple aggregates (in order to support atomic commits to the event store). On the other hand, the projections are only responsible for processing queries and returning values. A single projection, unlike an aggregate, may listen to events from multiple aggregates in order to support complex views. This example shows how a single projection spans multiple bounded contexts to support a rich query.
Thanks! that helps a lot. So projections interpret events to construct some representation of the data. And the answer to my earlier question about default values is that it depends if they are a property of the entity or default values in the representation that your query is building; which are two completely separate things. Got it.
Would it make sense to use this library if I plan to separate my bounded contexts across multiple microservices (each running in it's own AWS lambda function)? As I understand it, if my projections span multiple bounded contexts, then they would need to be in some distinct microservice which subscribes to all the events required to build those projections.
I designed this package to work with either a monolithic application or a suite of microservices. In your lambda functions, you will need to create an instance of the domain class (see this) with any event stores and projection stores needed for that particular service. It may be easier to just create the instance with all of the externals in every service. This package will derive dependencies on the fly based on your projections, and only query event stores that are required.
So defining a projection
within a domain instance is requires that all events processed by that projection
are also defining within aggregates
of that same domain instance? or can they be separate.
Also, are projections also built by 'folding' some past events? or does it just store the current state and apply the current event to it.
Another quick question: The output of aggregate events (e.g. ideaCreated
), is that "given the current state + this event, return the updated state" i.e. do we return the updated state? or do we return some transformed event to be stored and replayed back later
The names of the aggregates and projections do not need to match. The projection states are built the same way that the aggregate states are built: by folding past events. Each aggregate function should be side-effect free (similar to redux reducers). The event handlers take the current state and an event, and return a new derived state. The command handlers take the current state and a command, and either accept a new event or reject with an error.
I will take some time this week to expand on the example application and add comments detailing each function.
Hey there,
I've been reading up on DDD and I'm trying to understand the way the example is structured.
So first, with the aggregates folder: Is an
aggregate
here equivalent to a DDD aggregate?I was wondering about value objects and entities and how they should be represented. I assumed that
idea
,project
, anduser
are each a root entity inside some aggregate, with value objects maybe added directly from the event, without being explicitly modeled in the domain.But then I looked at the projections folder and saw that
projects
andideas
are properties of theuser
which made me think that maybeuser
is the root entity andidea
andproject
are additional entities inside the same DDD aggregate, and that maybe aggregate here means something different.Moving on to projections:
projects
andideas
are initialized as empty arrays on theuserRegistered
event. Is that where default/initial values should be set? and not in thedefaultState
of the aggregate?Thanks so much for your patience and sorry if these questions are really annoying π