Re: DRY and cross domain logic

"Paul Rayner [email protected] [domaindrivendesign]" <[email protected]> Fri, 22 May 2015 09:19:11 -0600
Newsgroups gmane.comp.programming.domain-driven-design
Message-ID <CAKKp0qgO3udGYLfzfEJRqgkyYm6nSaDFW3T6Hykigyj_Seikdg@mail.gmail.com>
Conceptually, a repository is a species of service, but one dedicated to
persistence concerns for a particular aggregate.

My heuristic: If an aggregate is what is being returned then I would lean
towards a repository since a repository tends to function as a domain
service for persistence purposes (eg. providing the illusion of an
in-memory collection of all objects of an aggregate's root type). But if it
is a calculation being performed then a dedicated domain service would be
my preference.

I could go either way on this one. Whichever one you choose, the
implementation you've described sounds right.

Paul.

On Fri, May 22, 2015 at 8:48 AM, Tom Eugelink [email protected]
[domaindrivendesign] <[email protected]> wrote:

>
>
> Agree, however a domain service does not seem like the correct place. I
> would expect a setup similar to repositories, with an interface defined in
> Patient's domain and an implementation in Patient's boundary, which then
> can do the anti-corruption to the CalculateAgeService in Client's BC.
>
>
>
> On 22-5-2015 16:44, Paul Rayner [email protected]
> [domaindrivendesign] wrote:
>
>
>  Think about this in model terms. The CalculateAgeService belongs in the
> Clients BC, which means it speaks the Client BC language. You don't want
> that language bleeding into your Patient BC, so I'd recommend something to
> translate the language from one context to another and isolate that
> translation at the boundary. Perhaps it could start out as a domain service
> in the Patient BC which functions as an anti-corruption layer by wrapping
> the Client open host service. So, yes, call something within it's own
> boundary.
>
>  On Fri, May 22, 2015 at 8:31 AM, Tom Eugelink [email protected]
> [domaindrivendesign] <[email protected]> wrote:
>
>>
>>
>>  Duplication, within limits, was my initial idea as well, but my team
>> kinda goes beserk over duplication. And, as said, the algorithm may be more
>> complex and must stay within the one BC.
>>
>> Open Host Service, basically a call to another BC's API, that is what I'm
>> currently looking at. Would Patient.calculateAge() call the
>> CalculateAgeService in Client's BC directly? I feel entities should not
>> call out to another BC directly, but instead call something in its own
>> boundary.
>>
>> Tom
>>
>>
>>
>> On 22-5-2015 16:16, Jorge Branco [email protected]
>> [domaindrivendesign] wrote:
>>
>>
>> > Secondly, you could implement the logic in both bounded contexts. I'm
>> less concerned about DRY across bounded contexts, since we are talking
>> about different models.
>>
>>  That was my thought. Is everyone working on those 2 BCs or do different
>> teams each have their own BC?
>>
>>  I'd say over time it may actually be okay to have some code duplicated
>> in that situation, as it starts getting hard to have both teams in total
>> sync.
>>
>> On Fri, May 22, 2015 at 3:12 PM, Paul Rayner [email protected]
>> [domaindrivendesign] <[email protected]> wrote:
>>
>>>
>>>  I see (at least) three options, with various tradeoffs: Firstly, the
>>> Client bounded context could provide an Open Host Service that takes in the
>>> relevant info and reports out the result, which the Patient bounded context
>>> could then call. This seems the likely candidate if several
>>> entities/aggregates are required from Client.
>>>
>>>  Secondly, you could implement the logic in both bounded contexts. I'm
>>> less concerned about DRY across bounded contexts, since we are talking
>>> about different models.
>>>
>>>  Thirdly, share the logic in some way (abstract base class etc), which
>>> gives you a Shared Kernel. Shared Kernels are tricky to get right, and
>>> given what you've said about the algorithm and the potential need for other
>>> entities, I'd be wary of this one.
>>>
>>>   Paul.
>>>
>>>   On Fri, May 22, 2015 at 8:02 AM, Tom Eugelink [email protected]
>>> [domaindrivendesign] <[email protected]> wrote:
>>>
>>>>
>>>>
>>>>  But what if the input of the algorithm requires other entities in the
>>>> Client's BC? The output may be simple, but maybe the age calculation also
>>>> depends on the country where the client is born, because they use a
>>>> different calendar (e.g. Chinese). The country is not present in Patient's
>>>> BC.
>>>>
>>>> Tom
>>>>
>>>>
>>>> On 22-5-2015 15:09, Jose Fernandez [email protected]
>>>> [domaindrivendesign] wrote:
>>>>
>>>>
>>>> Just create  base class with common members and methods. Inherit your
>>>> AR from it.
>>>>
>>>>  public class Person
>>>> {
>>>> public DateTime BirthDate;
>>>> public int CalculateAge()
>>>> {
>>>> return age here;
>>>> }
>>>> }
>>>>
>>>>  Public class Client: Person, IAggregateRoot
>>>>
>>>>  Same for Patient.
>>>>
>>>> Sent from my iPhone
>>>>
>>>> On May 22, 2015, at 8:31 AM, Tom Eugelink [email protected]
>>>> [domaindrivendesign] <[email protected]> wrote:
>>>>
>>>>
>>>>
>>>> We have a discussion in my current project group about the following:
>>>> - There are two bounded context.
>>>> - In each bounded context there is an person-alike entity, in one BC it
>>>> is Client in the other it is Patient, mostly with their own business logic
>>>> and methods specific to their roles.
>>>> - However, at some point Patient requires some determining logic that
>>>> is implemented in Client. For simplicity let's assume both have a
>>>> date-of-birth and the to-be-called logic is calculateAge().
>>>>
>>>> This is not something that can be done with events, and because of DRY
>>>> you do not want to implement calculateAge() twice. What is the best option
>>>> to calculate the age of a patient? Client and Patient share a unique id, so
>>>> Patient knows which Client to get. (N.B. I seek a conceptual solution, not
>>>> one specific to the calculate age problem.)
>>>>
>>>> Tom
>>>>
>>>>
>>>>
>>>
>>
>>
>
>  
>