absolute-beginners-guide-to-ddd-with-symfony icon indicating copy to clipboard operation
absolute-beginners-guide-to-ddd-with-symfony copied to clipboard

Sharing data between bounded contexts

Open micc83 opened this issue 3 years ago • 9 comments

Hello @nealio82 ,

I've been following your talk at sfday last week. As I'm mostly a Laravel dev I tried to rebuild your example on Laravel and I think I did a pretty good job...

Well... until I got to adding an extra bounded context and trying to share resources between the two. Do you have any plan to integrate this repo with the missing steps or some resource to point me in the right direction?

micc83 avatar Nov 26 '20 14:11 micc83

Hey! Glad to hear you've taken it and got it working!

I was planning to update the repo when I have a bit more time, and expand on this to implement a few more features.

What kind of resources were you trying to share between them?

nealio82 avatar Nov 27 '20 18:11 nealio82

Hi @nealio82, in my case I used a slightly different scenario but to be consistent with your example you can imagine that after the apointment the vet needs to register the actual diagnosis.

In the diagnosis aggregate we will reference again a Pet model. Shall I redeclare the pet model inside the new bounded context? And assuming that the first one (Scheduling BC) was persisted (it wasn't) I'll simply map both to the same table in the repository implementation (infrastructure layer)? Also, shall the pet be persisted inside the repository of the root aggregate (diagnosys) or have its own repository?

Also in my example I added a notification sent through a send method of a Notifier contract. In my first BC I put the contract inside the domain (can sending a notification be considered a domain concern?) and the implementation inside the infrastructure:

- RegisteringDiagnosesBoundedContext
|- App
 |- Command
  |- RegisterDiagnosysAndSendNotification
|- Domain
 |- PetIdValueObject
 |- PetModel
 |- DiagnosysIdValueObject
 |- DiagnosysModel
 |- DiagnosysRepositoryInterface
 |- NotifierInterface
|- Infrastructure
 |- MailNotifierImplementation
 |- DbaDiagnosysRepositoryImplementation

The point is that for each BC I'll have to duplicate both NotifierInterface and MailNotifierImplementation. Is there a DDD way to share them between many BCs?

To close my main concerns are about code duplication. I realize that DDD involves some degree of code duplication by its own nature but when is it ok to share contracts/implementation between BCs? And if possible, how to do it?

Ps. The same goes for vendor packages. You reference Ramsey\Uuid\Uuid inside the ApointmentId. When is that ok and when it's not?

Thank you for your help.

micc83 avatar Nov 29 '20 11:11 micc83

... Ps. The same goes for vendor packages. You reference Ramsey\Uuid\Uuid inside the ApointmentId. When is that ok and when it's not?

Thank you for your help.

I think here you could just have something like App\Identity\Generator which has the dependency to Ramsey\Uuid\Uuid and therefor depend only on your own Generator within your namespaces?

nimmneun avatar Dec 04 '20 11:12 nimmneun

@nimmneun So App\Identity\Generator would be global to all BCs? And how it would be instanced inside the Value Object? Sure that way I wouldn't have dependencies on the outside world but I would still create a layer of dependencies on a common ground between all my BCs. Would that be right? Would'it actually be easier to depend on a composer dependency if, for example, I should move a BC to its own microservice or composer package? Thank you for your help.

micc83 avatar Dec 04 '20 13:12 micc83

Would'it actually be easier to depend on a composer dependency if, for example, I should move a BC to its own microservice or composer package?

Personally I prefer to depend on my applications internals / interfaces e.g. a super blunt approach could be class App\Identity\Generator extends RamseysFactory implements App\Identity\GeneratorInterface or something like that. It gives me the flexibility to switch to whatever uuid generator in the future, without having to worry about implementatino details in my domains that use the generator.

I usually also have a namespace like e.g. Shared\Http\Client with HttpClient extends Guzzle... implements MyHttpClientInterface etc etc. but I'm also still in the very experimental phase with DDD 🙈 ... which has not changed the past 2-3 years 🙈

nimmneun avatar Dec 04 '20 14:12 nimmneun

@nimmneun I see. So you'll have a set of common services shared with single BCs using interfaces. I see how this is probably a good way to go even if I'm left with the doubt of how I will track all these internal dependencies once the app grows larger. Also I would use composition over inheritance in this scenario so that I can freely model my own interfaces independently from the actual implementations.

micc83 avatar Dec 04 '20 14:12 micc83

Hey @micc83, sorry I didn't reply sooner!

In the diagnosis aggregate we will reference again a Pet model. Shall I redeclare the pet model inside the new bounded context?

Yes. In the new BC the pet is a completely different concept. All they share is the name, but their roles and functionalities are totally different!

And assuming that the first one (Scheduling BC) was persisted (it wasn't) I'll simply map both to the same table in the repository implementation (infrastructure layer)?

Mapping to the same table is probably a bad idea. Imagine this - you realise that the appointment bounded context would make a perfect boundary around a microservice, and you decide to put that micro service behind a REST API. If it shares the same DB table as another BC, this would be really difficult to do, not to mention that you'd be by-passing all the good work you did in the PHP code just to stitch it back together into a mess in the DB!

Also, shall the pet be persisted inside the repository of the root aggregate (diagnosys) or have its own repository?

Again, separation of concerns is better :)

The point is that for each BC I'll have to duplicate both NotifierInterface and MailNotifierImplementation. Is there a DDD way to share them between many BCs?

There are different approaches to this and you can make a shared helper package. It's often faster to do something like that and experiment. You could create a Notifier package which you pull in via Composer and wire with the framework, then you only need the Interface within each BC.

To close my main concerns are about code duplication. I realize that DDD involves some degree of code duplication by its own nature but when is it ok to share contracts/implementation between BCs? And if possible, how to do it?

Duplication is ok - as long as you understand why it's being duplicated. If you have the same thing in a few places, you probably have cause for extracting some sort of external package. Or re-evaluate your BC boundaries, because if they all do the same thing to the point that they all share the exact same code, then maybe your BCs could be drawn some other way...

Ps. The same goes for vendor packages. You reference Ramsey\Uuid\Uuid inside the ApointmentId. When is that ok and when it's not?

It's up to you to decide. I used UUID because it's stable and low-level enough for me to consider it as a language feature. I definitely wouldn't do it with anything with more rich functionality. You can make the decisions, but you need to understand why you made each trade-off :)

nealio82 avatar Dec 04 '20 19:12 nealio82

Thank you so much for your thorough explanation. You've actually shed some light on my doubts. I'll try to apply your advices to my own Laravel project and see how it goes. I'll eventually share back what I've learned from it. In the while if you should expand your example it would surely be appreciated. Not sure if I should close the issue or keep it open to dig further on the subject (in case someone else should join). I'll leave to you! Have a nice week end!

micc83 avatar Dec 04 '20 21:12 micc83

Hello @nealio82,

thanks for clarifying the concepts it is very helpful!

Have a nice day!

miguel-serrano avatar Jul 05 '21 15:07 miguel-serrano