proophsoftware / es-emergency-call

Struggling with CQRS, A+ES, DDD? We can help you!
BSD 3-Clause "New" or "Revised" License
26 stars 0 forks source link

Need help with finding aggregates and bounded contexts in our domain #7

Closed enumag closed 5 years ago

enumag commented 5 years ago

Hi everyone, here is the issue I promised to create in https://github.com/prooph/event-sourcing/issues/79.

Note that I'm going on vacation this saturday and will only have a limited ability to answer questions. Feel free to postpone reading this until August 18th or so when I'll be back to answer anything you might wish to know.


draw.io diagram: https://drive.google.com/file/d/19DbQy1YJuU2klJF9nBa3bGHYI8oVA6UU/view?usp=sharing

EDIT: This might be better quality: https://user-images.githubusercontent.com/539462/43907077-04feb6fc-9bf5-11e8-85f5-f15c5ae20f5f.png

In the draw.io representation I went for something close to an UML diagram, but focusing just on the relations. The diagram represents what a relational schema would look like - in fact this is pretty much what our PostgreSQL projection looks like. I did cut a few less important things here and there such as that each ContactCard hold some addresses, phones, emails etc - these details are not important for this issue in my opinion.

The purpose of this issue is to help my team determine what the aggregates here are and where are the bounded contexts boundaries because based on previous discussions with prooph comminity I don't think our current model is correct. (Which is to be expected since our experience with DDD is limited.) Ideally I'd like to create a case-study based on this to provide an example to others who are new to DDD.


Now to explain the business, what the entities and relation mean and what processes are there.

The business is about providing accountancy services to the owners of large buildings or organizations that take care of the building when each apartment is owned by a different person.

First let's say the database is empty so we need to add some data. The first thing to add is a building, it's entrances (each entrance has a different address) and the apartments in the building. Then we need to add the contracts - who owns and who lives in each apartment. These people need to pay some fees every months which is where our company comes in - to help determine what fees they should pay and observe if they are indeed paying them. When adding a new building, most of the data about apartments, contracts and people are loaded from an external database or an import file.

Next we need to tell the system what fees should the people pay and how to calculate the exact amounts (since they can differ each month but can be calculated). This is represented by the FeeRecipe entity. Each contract usually has around 5-10 FeeRecipes. As you can see in the diagram there is a FinancialAccount entity between Contract and FeeRecipe. Most often each contract only has one FinancialAccount but in some edge cases there can be more. This is required because the Fees on different FinancialAccounts are treated a bit differently in some cases based on the attributes of the FinancialAccount but the details are unimportant.

At the end of each month an automated process takes each (still active) FeeRecipe and generates a new Fee for it for that month. The calculation requires the formula from the FeeRecipe and the current ApartmentAttributeValues on the related Appartment. ApartmentAttributes are properties of the apartment such as bodycount (how many people live in the apartment at the time) which can change every now and then. Each change is represented by a new ApartmentAttributeValue.

Next looking from the other side of the diagram there is an AccountingOrganization. In most cases an AccountingOrganizations is 1:1 with Building but there are exceptions with one AccountingOrganization handling multiple buildings and also exceptions with one building being split to multiple AccountingOrganizations. Therefore we made them completely independent.

FinancialAccountGroup is actually a brand new entity we came up with just this morning after discovering some serious problems in our PaymentAllocations process. Basically it's a group of FinancialAccounts that holds some options how should the Payments be allocated to Fees. FinancialAccountGroup is actually the scope for the PaymentAllocations process - take all not-fully-allocated Payments and FeeItems related to the FinancialAccountGroup and do the process described in the previous issue.

PaymentPreference holds a reference number that we can use to match a given payment from the import to a specific person and FinancialAccountGroup. The process of matching imported payments to people converts UnassignedPayment to Payment.

Finally the ContactCard represents a Person or Company and can be referenced pretty much anywhere in the system. It can be an employee of our company, an owner of an apartment, an organization managing a building, a contractor our company cooperates with etc.


Our struggle mostly comes down to the uncertainty of "how big should an aggregate be" - where the answer is of course "it depends" but I'm unsure "what exactly it depends on". Should each of the entities in the diagram be an aggregate? If not then how far should one aggregate go? Is there some limit to how much data should an aggregate hold or how many different methods, related commands and events it should have? For now most of the things on the diagram are actually aggregates (with a few more aggregates planned to oversee the automated processes) with the structure being split to around 5 bounded contexts. Is that too many?

Another problem is that some parts of the structure are pretty much pure CRUD - Building, Entrance, Apartment, ApartmentAttributeValue, Contract, FinancialAccount, FeeRecipe, ContactCard - all of that is pretty pure CRUD, albait some parts are automated with imports. The reason why we chose to use DDD are the processes around generating fees, finding out which payment was payed by who and which payment pays which fees (the last process is explained a bit more in https://github.com/proophsoftware/es-emergency-call/issues/6). Also having the entire event sourced history will be beneficial in some of the processes and also other parts that are not yet represented on the diagram (such as calculating penalties for late payments). This puts us in a not very good position where we need to deal with CRUD in ES/CQRS while knowing that ES/CQRS is not really the best thing for CRUD.

To give some numbers a building typically has 1-5 entrances, an entrance can have up to a 100 or so apartments, each apartment has a about 1-3 contracts active at any given time (+ many historical contracts that already ended), and each contract has about 10 fees generated each month.


I didn't write down our current aggregates and bounded contexts here but as said above, most entities on the diagram are in fact aggregates now. Bounded contexts are RealtyRegistration, FeeCalculating, PaymentCollecting, ContactAdministration, Accounting - I feel it's unnecessary to tell you what's where since it's most likely wrong and you can probably imagine what's where anyway.

I have some ideas on my own about where would I split the aggregates now after getting some experience, but I'm not confident enough about it to start refactoring the application that way. And I'm still very unsure about where to split the bounded contexts. Here are some of my current thoughts:

I sometimes feel like a Building should be an aggregate (containing Entrances, Apartments, possibly even Contracts?) but then I remember that one Building can have several hundreds Apartments and it starts to feel too large. Then maybe a Contract aggregate (containing FinancialAccounts, FeeRecipes, Fees and PaymentAllocations) - but some people can own an apartment for decades, sending hundreds of Payments and paying (or maybe not paying) thousands of Fees. And even such giant Contract aggregate still can't generate new Fees on it's own because it doesn't have ApartmentAttributeValues. Should the aggregate also contain Apartments as well then to be able to calculate Fees? But that would make it even bigger.


Now regarding what you told me in https://github.com/prooph/event-sourcing/issues/79 that I should check for existence of related entity IDs not in the command handler but only after storing what happened into EventStore. As you can see, nearly every entity in the system holds an ID of one or more other entities. For example a Contract holds an ID of the Apartment and then up to 4 IDs of ContactCard (SuperiorParty, SubordinateParty + each of them possibly represented by another ContactCard, such as a company owning a building being represented by one of their employees).

It just doesn't feel right to me to deal with non-existent entity as explicit part of the business process as @fritz-gerneth suggested. If used that approach to every such problem it would require me to write a lot of additional code to handle such events and negate anything that was based on that invalid data. There isn't any business to do about it either - the system got an invalid ID either due to some mistake of the client working with it or on purpose from a hacker. It doesn't make sense to ask them for anything, we just need to negate everything that happened since then and I can't help but feel we should have prevented it from happening instead.

Let's say someone added a new Contract with nonexistent ID of the SuperiorParty ContactCard. Then before the error could be discovered they sent commands to create FinancialAccount, FeeRecipe, generate a Fee from the FeeRecipe and to allocate an existing Payment to it. Then the system finds out the Contract should not exist and needs to revert not only all of that but also any PaymentAllocations that happened since then because they should have been allocated differently. Dealing with all that feels like a total overkill for a simple missing ID case. But of course feel free to convince me now that you know the context, I most likely missed a lesson again.


That should be about everything important I can think of right now. Feel free to ask any questions and thanks in advance for any help you can give me. Also, where can I find a donate button?

codeliner commented 5 years ago

Also, where can I find a donate button?

There is no donate button ;). The price you have to pay is that you need to share all information publicly, which is often not possible because exposing domain logic does mean that competitors can learn from your domain. Most businesses don't allow that.

Therefor it is hard to find real world domain examples except the ones used in DDD books. But each domain is different so the examples are still only examples.

The second disadvantage is that there is no SLA. We can try to help, if we have enough spare time. The more complex the problem is, the longer it will take to understand the problem and work out a solution. This process might not match with your project deadlines. For example I'm not available the next weeks and when I'm back online I'll have a lot of paid work on my desk which definitely has higher priority.

Having said this, if finding a solution is time critical you might be better of with a paid DDD consultant. If time is not a problem, we can try to help. For now I'd ping a few people and see if one can take over during my offline time.

The thing is doing all this remote is hard. Let's see if we can still come up with a good solution ;)

codeliner commented 5 years ago

Ok made some noise: https://twitter.com/prooph_software/status/1027672367902941184

What we need:

enumag commented 5 years ago

The price you have to pay is that you need to share all information publicly, which is often not possible because exposing domain logic does mean that competitors can learn from your domain. Most businesses don't allow that.

Yeah, I got a permission from my boss. It's fine as long as we don't share source code and not delve too deep in our algorithms but those details are not important here anyway.

It's not really time critical to solve this issue. The time critical thing was to get some understanding how to implement a complicated process which you already helped us with in the previous issue. We'll have to implement those processes first anyway and finish some more features before we can get to any refactoring based on this issue.

This issue is mainly meant for me and my team to learn more about DDD. It's not only about getting some advice for our domain but also about knowing the reasons.

Thanks for any help.

codeliner commented 5 years ago

cool, perfect base for a case study

codeliner commented 5 years ago

@enumag

Can you describe for each "CRUD" entity (Building, Entrance, Apartment, ApartmentAttributeValue, Contract, FinancialAccount, FeeRecipe, ContactCard):

If you can provide some organisational information about your company that would be nice, too (if not possible, because you are not allowed to expose those information, then skip the question):

enumag commented 5 years ago

Where do you get the data from?

When possible the data are imported from a file (let's say XML) or from an external database. When not possible they are added in manually by our employees.

Later modifications are all done manually as far as I know.

Who is responsible for the data?

Our emloyees, although I'm not sure if that's what you're asking.

How often does the data change?

Not too often. Usually just when an apartment is sold. Other than that people can report changes in their contacts, addresses etc.

What data changes influence the fee generation process and how?

Any change on FeeRecipe and any change on ApartmentAttributeValue. Possibly something on Apartment but that should be all.

EDIT: Forgot to answer the how part. Change on FeeRecipe usually means change to the formula to calculate the fee. Change on ApartmentAttributeValue means a change to the variables used in that formula. We're using Symfony/ExpressionLanguage to calculate the actual fee amounts from this data.


The company is not too large so 1 dev team, couple of employees who work with the system, each taking care of a few buildings. We will surely integrate with 3rd party APIs to import some data and the plan is to also provide our own API in the future - that's why the backend is GraphQL application with frontend being a JS client running in the browser.

codeliner commented 5 years ago

Ok, I've put together a summary in a gist. You can find it here: https://gist.github.com/codeliner/0204894c29173e8d0aa24bff0d4e88a3 @enumag You can read through it and if you think I've missed some important information then please add a comment to the gist.

I tried to group information from your explanation, sort them and highlight some key phrases, concepts, attention points, questions ... Don't consider this as complete, correct or whatever. It IS ONLY my current understanding based on the information you gave me.

Our first step now is to work out a context map. The good news is, that we can keep the context map simple because only one team is working on the system and as you told us the company structure is also relatively flat with a few people working with the system.

Also the context map we are drawing is not a final thing. Whenever new concepts arise for example integration of external systems, growing company, new software modules .... You have to revisit the context map and check if it needs to be aligned.

One of your questions:

And I'm still very unsure about where to split the bounded contexts. Here are some of my current thoughts ....

Please read this article about DDD context maps: https://www.infoq.com/articles/ddd-contextmapping and based on the explanation in the article try to draw such a map for your domain.

You've named 6 bounded contexts. I tried to match your explanation and the contexts in my summary (see headlines). I've also marked each context as either supporting or core. Here is a short blog post about the classification: http://blog.jonathanoliver.com/ddd-strategic-design-core-supporting-and-generic-subdomains/

Attention My assumptions are maybe wrong. Treat them more like questions and try to focus on the Ubiquitous Language to identify context boundaries (the linked article provides a good manual).

Maybe start by answering this question: What are the reasons for the current 6 Bounded Contexts?

Answer for each:

Hint

ContactAdministration is a good example for a dedicated bounded context. You said:

Finally the ContactCard represents a Person or Company and can be referenced pretty much anywhere in the system. It can be an employee of our company, an owner of an apartment, an organization managing a building, a contractor our company cooperates with etc.

So one part of the system deals with managing those contacts. They are referenced in other parts of the system but have different meanings depending on the context where they are used.

enumag commented 5 years ago

@codeliner I'm about to leave and I most likely won't have internet connection for a week so I won't be able to read your comment and answer the questions. Maybe my colleague @tomcizek could? Really sorry about this, I did warn about my absence in the first post though so you're hopefully not too surprised. Thank you very much for your effort!

codeliner commented 5 years ago

@enumag no problem. Answer when you're back. As I said, I'll be offline the next weeks, too. Just wanted to trigger context mapping. You'll need some time for it anyway ;)

codeliner commented 5 years ago

@enumag did you find the time for the context map?

enumag commented 5 years ago

@codeliner I had some other priorities after the vacation but I'll try to make it next week.

enumag commented 5 years ago

So first to answer the question about the reasons for the current bounded contexts:

Next I'll try to draw a context map with these bounded contexts.

enumag commented 5 years ago

There is one thing I forgot to mention about the domain graph I posted earlier.

In the graph some of the entites are red, some are blue and some are purple. I forgot to mention what those colors were about.

These colors are related to the security - defining privileges who can manipulate what entities and how. We need the privileges to not be system-wide but rather building-wide. But things like payments are not related to one building - when they are imported it's unclear to which building they belong and when they are allocated then one payment can be allocated to fees in multiple buildings. So instead these things have accountingOrganization-wide privileges.

enumag commented 5 years ago

As for current context map, that's actually pretty simple:

RealtyRegistration ---- FeeCalculating ---- PaymentCollecting ---- Accounting

Plus ContactAdministration which is connected to all four of the above RealtyRegistration, PaymentCollecting and Accounting.

I'm not really sure about the relationships between them. The article provides some examples but no clear definition what possible relationships there can be between bounded contexts and how to recognize them. The bounded contexts are not microservices communicating via API so I don't think they are upstream-downstream, right? So is everything just partnership or what? I'm afraid I'm missing something here - I don't know how to qualify the relationships. Really sorry that I can't provide anything better. Maybe you can push me in the right direction?

enumag commented 5 years ago

Maybe I should try to write down the relationships somehow. You could try to advise me with the qualification.

codeliner commented 5 years ago

thx @enumag

I try to answer until end of week

codeliner commented 5 years ago

@enumag I think we are on a good track. Regarding the Context Map: we can keep it simple. To quote from the first article:

So far, we considered a simple scenario with only one development team. This allowed us to ignore the cost of communication, assuming (maybe optimistically) that every developer in the team is aware of "what's going on with the model". A more complex scenario might include some of the following influencing factors:

  • domain complexity (requiring many different domain experts)
  • organization complexity
  • longer projects (time)
  • very big project (person days)
  • many external, separate or legacy systems involved
  • large team size or multiple development teams
  • distributed or offshore teams
  • human factor

That said, upstream/downstream etc. has less to do with microservices but more with team organisation and integration of external systems. A context map helps to identify these, but because a single team is working on the system and data finds its way into it through imports or manual editing, we don't need to classify the relationships.

So can you draw a simple context map based on the relations you've written in your last comment?

FeeRecipe Actually there must have been some misunderstanding between us. FeeRecipe is not a bounded context. It's an aggregate inside FeeCalculating bounded context.

Ok, let's see if this is still the case after Event Storming. I've put it in its own bounded context based on your initial explanation of the domain, but as I said. This can be totally wrong.

In general: Do you know the statement:

All models are wrong but some are useful.

Don't worry too much about the current design. It worked so fare, so it is useful. What we do here will also produce a wrong model. New requirements and new knowledge about the domain will change the shape again.

https://twitter.com/jessitron/status/1040284446824509440

Before I can answer your questions regarding the correctness of the bounded contexts we need to do a second activity. We need to event storm the high level business processes. In the previous issue we did that for payment allocation, so we zoomed into one process and discovered it: https://gist.github.com/codeliner/97ef981147a95a3096c7c89d6dd3d32c

But we need to zoom out again and start on a high level without the details. Start with imports of building, contacts, etc. and draw a timeline of important events along the way.

enumag commented 5 years ago

Ok, let's see if this is still the case after Event Storming. I've put it in its own bounded context based on your initial explanation of the domain, but as I said. This can be totally wrong.

It might be correct but I don't yet understand your reasons for splitting this one aggregate into separate BC. I was just stating how it is currently implemented.

In general: Do you know the statement:

All models are wrong but some are useful.

Don't worry too much about the current design. It worked so fare, so it is useful. What we do here will also produce a wrong model. New requirements and new knowledge about the domain will change the shape again.

Yeah, I know about that. I'm well aware that the goal here is simply to maybe produce something a little less wrong and slightly more useful. It's mostly about learning how to analyze domains so that we can continue the tornado on our own and hopefully teach others how to do it.


Ok, I'll try to draw the context map and a high level overview of the timeline.


There is one more thing. Consulting some other people elsewhere, they said a few things that seem to suggest something along these lines: "Command + Aggregate data should always be enough to perform an action. If the action needs other data from elsewhere then it's not really a command and some refactoring is needed."

They didn't yet answer to confirm if it really is their opinion though. What do you think about this statement? If it is true it kinda contradicts what I'm doing so far and could be the core of what I'm doing wrong.

enumag commented 5 years ago

Current context map: https://drive.google.com/file/d/17gcgNg5-tHvqGaZB7BIks6UkrUHM011P/view?usp=sharing

enumag commented 5 years ago

Here is the timeline. The blue bubbles are the three processes we created separate aggregates for based on our previous discussion. Note that import of a new building is not implemented yet - we have enough data from the old system so it's not needed yet. Notifications are not implemented yet either.

Let me know what should I be more specific about.

timeline

fritz-gerneth commented 5 years ago

Command + Aggregate data should always be enough to perform an action.

Any aggregate root you have foremost is a consistency context. If any command requires outside data to be viable this suggests that your command (and the action it takes on the AR) is directly depenedant on the other data source. Its state (and invariants) become dependent on something else and the aggregate root cannot protect all invariants anymore. That's why a command in its pure form should not do this.

So far the theory. I have seen a few places where this still might be acceptable:

To conclude: all domain commands should be self-contained. All data required for the operation on the aggregate should be part of the command (or the aggregate already). If not this often suggests your application layers are leaking into your domain.

codeliner commented 5 years ago

I think we need a tool that tracks discussions and documents along the way like we do here within a github issue, but this tool would allow branching of discussions.

The "commands should be self-contained" thingy is an interesting question but it is not the right time yet to discuss it. We should focus on the model and domain behavior from a business POV. Once we're satisfied we can turn that into software and look for design patterns that help us.

codeliner commented 5 years ago

thx @enumag for the timeline. I'll do an Event Storming based on it. Maybe I can set up realtimeboard for collaboration because Event Storming without interaction does not produce the same results. But I hope that it is still good enough to share a vision of the model, even if it is simplified and somewhat blurry. As you said, it's more about teaching the practice than developing the best possible model.

codeliner commented 5 years ago

ok, I played a bit with this board here: https://awwapp.com/b/u9blyfjog/#

Not really happy with the usability. realtimeboard is much better, but it does not have free boards where you can collaborate without an account.

I just started to add a few sticky notes. Feel free to continue or edit. If you want to add a note it is best to copy an existing one then change text and color.

enumag commented 5 years ago

Commands / Events that are not domain commands/events but application commands/events. Of course those might trigger a domain command on their down with the data loaded :)

@fritz-gerneth Could you tell me more about these "application commands/events"? In what ways are they different from "domain commands/events". It's not like I never heard of them but the meaning is vague for me.

For example let's take the payment allocations process from my previous issue. When a user clicks a button to start the process the first thing that happens on the server is the command handler searching the projections to see which payments and fees will be a part of that process. Since the command results in new PaymentAllocationsProcess aggregate being created, I guess it counts as a domain command? How would you refactor it to get rid of checking the projections in command handler? I can't let the user send me the data - I couldn't trust them.

enumag commented 5 years ago

@codeliner Yesterday I saw something on the board you linked but it seems empty now? Did you delete it? Anyway maybe it would be better to use realtimeboard. Free version allows three collaborators which should be enough in this case.

codeliner commented 5 years ago

@enumag oh ok, the service is really not the best. Didn't touch the board since yesterday. Empty for me as well :( Ok, can you create a board on realtimeboard and invite me? I already have three collaborators connected with my account. use: kontakt at codeliner dot ws

enumag commented 5 years ago

Ok I created a new board and invited you.

Here is a public link to the board for anyone who wants to see the results (no login required but right now the board is empty): https://realtimeboard.com/app/board/o9J_kzYG5GQ=/

codeliner commented 5 years ago

thx @enumag , unfortunately this week is a little bit crazy so I can't fill the board until next week.

enumag commented 5 years ago

@codeliner Of course, no rush. Should I try to fill it with something myself? What kind of sticky notes should I focus on? I'm currently reading some articles such as this one to help me better understand the process.

codeliner commented 5 years ago

Basic colors/types basic_colors

start with events and add other types if needed for understanding.

enumag commented 5 years ago

I'm guessing that for the purposes of this event storming I should ignore how the current system works, right? So instead I added in the high-level events based on the timeline I posted above. Is that ok? What should I try to do as the next step?

Adding commands aggregates etc. doesn't feel right since most of these events are high-level and won't actually exist in the system...

codeliner commented 5 years ago

Normally your team would be in a room with one or more domain experts and would use the high level events to start discussions around them by zooming into the processes to add more detailed events + other types based on the explanations of the domain experts.

Unfortunately, we can't do that remotely. You could try it in your company and update the board with the results.

If that's not possible we use the high level events + our previous discussions as a basis to rethink the bounded contexts, identify the core domain and look at the aggregates and processes of the core domain. While doing this we can revisit the board and add stuff to keep discussions focused on the business model.

enumag commented 5 years ago

The one who knows the most about the domain is actually one of the dev team because he worked on the original system and knows all its problems. He just left for a vacation though.

Another issue with this is that we have already sort of done it when we started working on the system so we would most likely just repeat our previous mistakes.

I can try to zoom in on the events myself next week but... our previous attempt ended up with mostly CRUD events which doesn't seem to be the right way. I'm not quite sure how to avoid doing that again - most of the things happening there indeed are CRUD. Or maybe it's correct to focus on the CRUD things since that's what's causing most of the issues in the current system? What do you think?

codeliner commented 5 years ago

That's the problem @enumag If your domain expert is a developer you end up with a technical view instead of the business view. Business people (let's say a sales person) is not interested or doesn't even know about CRUD, Aggregates, Microservices, etc. Event Storming is perfect tool to keep the focus on business processes.

I still don't have much time, but I try to give a short example based on the current board: A dialog between two actors: the developer (Dev) and the domain expert (Exp)

Dev: "What about this Fee recipe added. Who adds those fee recipes?" Exp: "Well, our realty administration team is responsible for adding fee recipes." Exp puts a new yellow sticky note with realty administration team in front of fee recipe added note. Dev: "And does it happen that they need to change existing fee recipes?" Exp: "As long as no fees were generated for a fee recipe it can be changed, yes. People can make mistakes and correct them, but if fees were generated we need to know which recipe was used." Dev: "Ok, but what happens if a mistake is first noticed after fees were generated?" Exp: "For the next month a new fee recipe needs to be added. We keep the old one as reference, but the new one becomes active. Also the customer service team will contact the person and apologize for the wrong invoice. We'll pay back the difference." Dev: "I see. Let's add the process on the wall." Dev and Exp add more sticky notes ....

No word about transactions, entities, consistency and all the technical stuff. Basically, we try to identify what happens over time. Which business processes need to be supported by the system. Who uses the system and in which situations. If we have a good understanding of it, then we can start talking about implementation details like CRUD etc.

So yes, trying the same modeling process again without a non technical (real) domain expert will likely produce the same unsatisfying result.

What can you do? Ask the people who work with the existing system about the processes. What do they expect from the system. What features would help or support them in their daily work? Do an Event Storming session with them. Just try it!

Who is responsible for payment allocations? Ask them the same questions. And so on.


The explanation above is my advice for the real world. What we can do now is that you play the role of the domain expert and I play the role of the developer. I don't know much about the system yet. I talked to a developer already and they told me about the current modules of the system and gave me an overview of the domain. But I still try to understand how the pieces work together and I hope that you can shed some light (and update the board accordingly ;))

My first questions: Who adds those fee recipes? What happens if a fee recipe has a mistake?

enumag commented 5 years ago

@codeliner Now this is for sure some import notes to remember.

The non-programmer domain experts are a busy with other work most of the time so we can't have them brainstorming all the time. But we're certainly trying to think about the system from their point of view. Also whenever we're unsure what they would do in certain situation we consult with them.

For fee recipes you actually got it pretty close. There are some nuances about what to do with already generated incorrect fees. I tried to added some notes to the board regarding that. Hopefully that should answer your questions. I tried to use the color scheme from your comment above (+ I added light green for conditions).

There is one new thing for you here - "accounted for fees". Usually in april we need to wrap up the previous year. When that happens some documents are created and most actions are no longer available with the payments and fees for that year (and they become "accounted for"). We didn't delve too deep into that just yet - we have other things to solve before that.

There is one thing though about the notes I just added to the board. While these actions are needed to fix incorrect fee recipes, they are not specific to that process. It's just some tools the building referent (employee of our company who works with the system) needs to have at their disposal. For example unallocating and reallocating payments is also done when we discover some old payment that was not added into the system because of a mistake. So while I added the notes close to fee recipe addition for now, it doesn't really feel correct to have it that way. How should things like that be represented on the board?

codeliner commented 5 years ago

thx @enumag I'll check the board and your comment at the weekend.

It's a good time to link one of the best DDD talks (I'm aware of) in case you don't know the talk: Cyrille Martraire — Interviewing Domain Experts: heuristics from the trenches

codeliner commented 5 years ago

@enumag really good progress on the board :+1:

So while I added the notes close to fee recipe addition for now, it doesn't really feel correct to have it that way. How should things like that be represented on the board?

There are no strict rules. We only work with sticky notes so we can move them around, remove some or duplicate others.

Just add the other scenarios like this one:

For example unallocating and reallocating payments is also done when we discover some old payment that was not added into the system because of a mistake.

and copy & paste unallocating and reallocating payments notes.

I guess unallocating and reallocating payments is also done when an ApartementAttributeValue is wrong? Can you add that scenario to the board as well?

enumag commented 5 years ago

Oki, I'll try to add some more scenarios next week. I'll try to find some time for that talk you linked, didn't see that one yet.

codeliner commented 5 years ago

Side note: Event Storming is like brain storming. You add every piece of information to the board and rearrange as needed. Then we can use the notes to identify contexts by grouping them. But first we need them on the board. Still looking for the exact border of the core domain. One thing is for sure. Event Sourcing is a really good choice here, at least for the core domain. But maybe your team can use CRUD-ish approach in the supporting sub domains. I don't want to start a technical discussion yet, but let you know what I think will be the end result of this thread. Let's add the notes from my previous comment to the board and then look at the tactical side of DDD.

codeliner commented 5 years ago

I'll try to find some time for that talk you linked, didn't see that one yet.

:+1: @cyriux is a really good entertainer, too. So the talk has both: fun and value! Absolutely worth watching.

enumag commented 5 years ago

There is one more thing on my mind. In our previous eventstorming we mostly focused on adding the data we need into the system and the common tasks performed with those data. We didn't talk too much about these scenarios when something went wrong and needs to be fixed because they are not too common and the goal of the system is to help the system admins make them even less common. From this point of view, I have to ask - are you sure that there problematic cases are what we should focus on? Or is it exactly the point of doing event storming to focus on such scenarios despite their rarity in production?

Combining CQRS with CRUD-ish subdomains was one of my early ideas. I scrapped it because I didn't have nearly enough knowledge to decide what should go through CQRS and what shouldn't - and I still don't, never mind how actually combine them in the code. I'm very interested in learning more about that. Especially how to separate an event-sourced and non-event-sourced parts of a system. It seems quite impossible at first glance since the event-sourced part will need some data from the rest.

codeliner commented 5 years ago

are you sure that there problematic cases are what we should focus on?

That's a very good question! Let me try to explain: By asking questions like "What happens if ...?" "How do you handle that scenario?" "Why are you doing it?" ... you trigger discussions with the domain expert and learn more about the domain.

Look at my two questions and your answers:

1) Who adds those fee recipes?

It's just some tools the building referent (employee of our company who works with the system) needs to have at their disposal.

Great! Now I have a name for the actors working with the system. I can add it to my vocabulary and use it in further discussions. It's now part of the ubiquitous language.

2) What happens if a fee recipe has a mistake?

There is one new thing for you here - "accounted for fees". Usually in april we need to wrap up the previous year. When that happens some documents are created and most actions are no longer available with the payments and fees for that year (and they become "accounted for"). We didn't delve too deep into that just yet - we have other things to solve before that.

One simple question and I learned a completely new detail about the domain. That's great. I can take it into account when thinking about the different parts of system.

Keep in mind that our async, remote communication here is not comparable to a real event storming session. In the real world we would only need a couple of hours. If all ppl are in the same room and you have unlimited modeling space something magical can happen ;). The talk I've linked covers some more tips and tricks to interview domain experts. Event Storming is not the only way, but a very efficient one. See the notes I've used at the beginning of the thread: https://gist.github.com/codeliner/0204894c29173e8d0aa24bff0d4e88a3

In the gist I took your explanations and highlighted important words (UL). Grouped the information etc.

I've done it because I need to get an overview of the domain. We need the same understanding of the business. You are my domain expert at the moment. I learn from your knowledge, then I can build a system that supports your work.

Ok, let's move on: I already know that ApartmentAttributes are very important for generating fees but I don't see them on the board. Hence, I asked the next question to trigger a discussion about this concept ;)

But you are absolutely right that we should not discuss every little detail. During an event storming session you can add a "hot spot" note and move a discussion about the details to a later time. I've updated the board with such a hot spot marker. You're right. We are all very busy and we need to get as much information as possible in the shortest possible time.

Having said this, I'd like to ask another question:

You told me about ApartmentAttributes. They are assgined to a Building by the responsible building referent when a new building is added. You also said that those ApartmentAttributes can change, right? There is this monthly automated process that generates fees using the FeeRecipes and ApartmentAttributes assigned to a building. Is there some kind of "monthly deadline" for building referents until ApartmentAttributes changes should be made, so that the automated fee generation process can work with a fixed set of information? Can you add the processes regarding ApartmentAttributes to the board?

enumag commented 5 years ago

Sorry, I'm a bit busy this week rewriting our GraphQL layer to something easier to maintain. If it goes well I might even write an article how to use Prooph with GraphQL.

I'll answer when I can but it will most likely be next week.

MorrisJobke commented 5 years ago

Sorry, I'm a bit busy this week rewriting our GraphQL layer so something easier to maintain. If it goes well I might even write an article how to use Prooph with GraphQL.

That would be interesting to read 👍

enumag commented 5 years ago

I guess unallocating and reallocating payments is also done when an ApartementAttributeValue is wrong?

I see I didn't exactly answer this question.

If we find out that a unit attribute changed several months ago and wasn't reported we need to unallocate the payments since then, delete the generated fees for that period, generate new fees using the correct values and then reallocate the payments. So it's actually even more complicated than the previous case because we need to delete the wrong fees too.

EDIT: The answer above was actually incorrect. After checking with my teammates I found out it works differently. It's not necessary to change the old fees in this case because the difference will be calculated with when we wrap up the year the next april. Basically it doesn't matter if the people paid something wrong, it will all be fixed in this yearly audit but we don't need to actually change the old fees in most cases.

Is there some kind of "monthly deadline" for building referents until ApartmentAttributes changes should be made, so that the automated fee generation process can work with a fixed set of information?

No because there is no way to tell if we received all the changes or if there even were any changes. I'll double check with my colleagues though.

EDIT: Yeah, no deadline here.

Can you add the processes regarding ApartmentAttributes to the board?

Sure, I'll try to find some time today - have been neglecting this for too long.

EDIT: Considering that the history doesn't change in this case there is no process. Just change the ApartmentAttributeValue with the date when it did change and you're done.

codeliner commented 5 years ago

@enumag Do you have some example data? A building + entrance + apartment + apartment attribute values + fee recipe

And a simple/basic example of a fee calculation?

I've created an example project: https://github.com/proophsoftware/fee-office Next steps can better be discussed with code ;) I'd add a prototype using Event Machine in some contexts and Doctrine a document store in others. This will show you how you can split the system into contexts and use CRUD-ish entities in supporting subdomains and event sourcing in the core.

enumag commented 5 years ago

If I cut out the details that are irrelevant to this (such as some fields that are only needed to print them on documents) then I could generate some random data with Faker. I'm not familiar with Event Machine yet though. Where should I put the data and in what format?

codeliner commented 5 years ago

@enumag you can use a gist. Simplified sample data would be great. Just enough to build a prototype.

Oh and what type of contacts can exist? Some sample contacts would be great, too.

codeliner commented 5 years ago

@enumag regarding the format itself: JSON would be nice