Consider the following situation.
Three apps A,B and C must cooperate together: A is an external, third party app while B and C are in-house apps (so we have control over B and C, not over A). B replies to requests made by A, using both logics contained in C and B itself. Think about B as a layer between A and C.
There are some underlying common concepts that A,B and C understand and use.
Suppose the key task here is to decouple everything, so that if tomorrow we want to use A1 instead of A, all the interactions between B and C stay fixed (and, respectively if we want to use C1 instead of C, all the interactions between B and A stay fixed).
The question is about the datamodel design for B and C. Two solutions come to my mind:
- Shared datamodel: we introduce a datamodel D in a different project. D contains the "in house" version of the common concepts and is used by B and C: an A-version of the concept is mapped to a D-version and can be used and understood by both B and C. If tomorrow we want to use A1 instead of A, we simply re-write the adapter. If instead we want to get rid of C, we write C1 using the common datamodel D.
- Replicated datamodel: both B and C have its own version of the datamodel. We now have two adapters: one between A and B and one between B and C. If tomorrow we want to change either A or C then we rewrite the respective adapter.
Is there any best practice for dealing with this situation? Is there any alternative to 1. and 2.? Is there any intrinsic problem with either 1. and 2.?
Edit Following request I'll try to give a more explicit example (of course everything here is fictional. Also forgive my horrible fantasy).
ACME ltd is a used car retail company, needing detailed information regarding every car bought and to-be resold. This process is outsourced, so they expose a simple DTO with two classes ACME.CarInfoRequest
and ACME.CarInfoResponse
(containing proper fields). In particular there is the business concept of ACME.Car
.
ACME is outsourcing to INITECH inc, a car data provider. INITECH has a big and updated database containing car info and also features a live connection to the police records to check if the car is stolen. INITECH has one main app to interface with the customer and uses a different app to communicate with the police: the INIMAIN app and the INIPOLICE app. Both apps have the underlying concept of "Car".
The question is: should INITECH use a shared datamodel and let INIMAIN and INIPOLICE add it as a dependency or should implement mirrors in the two apps? In other words the two solutions could be:
1) INITECH builds INIDATA project. INIDATA contains INIDATA.Car
to represent the concept of "car". Both INIMAIN and INIPOLICE add INIDATA as a dependency and use the same INIDATA.Car
. When INIMAIN and INIPOLICE speak about a "Car", no translation is required (as they both refers to the same INIDATA.Car
). On the other hand, INIMAIN maps the info contained in ACME.Car
into INIDATA.Car
via an adapter.
2) INIMAIN has its own representation of INIMAIN.Car
(and respectively INIPOLICE has INIPOLICE.Car
). When INIMAIN wants INIPOLICE to ask the info about a car, first translates from INIMAIN.Car
to INIPOLICE.Car
. Then when INIPOLICE replies, INIMAIN translates everything back from INIPOLICE.Car
to
INIMAIN.Car
. Of course ACME.Car
is still mapped into INIMAIN.Car
via an adapter.
Hope it's more clear now (even if probably the example is awkward, again forgive my limited fantasy).