General
Purpose of this document is to define and explain how values can be mapped between an any LOB and any External Service which is contacted by the specific LOB.
Problem definition
Any Line Of Business (LOB) contains one or more sets of predefined values sets to name some specific situations. For example, set of values can define a status of an item - Active/Non-Active/etc.
The set of values is unique to that LOB only and serves it’s only specific purpose under same domain. There is a possibility that exactly the same set of values will be defined by other LOB(s), but in that case it should be treated as a completely separate set to avoid tight coupling between these multiple LOBs.
The problem appears when a third external and generic service have to interact with all LOBs and process their objects. In addition, the service needs to have some knowledge about status values coming in and out the LOBs and organize it into a common set of internal values.
The simple approach is to define a set of values inside the generic service for each LOB. But then the service is starting to be dependent on the LOBs and tightly coupled. Every time a status inside LOB changes, the service should be changed as well. The approach negates loose coupling principle of software engineering.
...
Explanation:
LOB defines a set of values, that is used inside an Object.
External service exposes an endpoint to interact with the LOB.
An Object on side of External Service have a number that represents “some” status. In order to know what status is it, the External Service should maintain a list similar to list defined inside LOB.
Solution
To decouple LOB from service, a mapper should be introduced. The new mapper service will get information from both sides - LOB and External Service, and configure a way to map values from each other back and forth. The mapping should be a one-to-one, i.e. be deterministic in any direction, without any ambiguity.
In such a way LOB will have it’s own values and External service will have it’s.
Below is the proposed solution:
...
Explanation:
One of possible and mostly popular modern solutions for the problem is to introduce a Mapper that will know values from both sides - LOB and External Service, define the way of mapping and act as a middleware while an Object travels between LOB and External Service in either direction.
The Mapper object will be instantiated on the side of specific LOB and will be dedicated to a specific set of values that have to be mapped. If there are multiple sets, then multiple mappers should be instantiated and configured accordingly.
External Service will expose an endpoint where it will send values of it’s internal list of values to a requesting LOB. The endpoint data will be used by a mapper to configure the mapping process.
Every time an Object is to be sent to External Service, it’s specific values to be mapped to values known by External Service.
Every time an Object is received by LOB from an External Service, it’s specific values to be mapped from External Service values to LOB values.
To do it successfully, the mapper should know both sides of the mapping equation - values from LOB and values from External Service. And the mapping should be configured in one-to-one relationship.
No need to remind that every External Service will require a separate mapper defined on LOB side.
Implementation
Mapper/LoBToServiceStatusMapper.cs was implemented inside WLM Demo Portal and is used to map Time Tracking Item statuses.
Constructor will require 2 dictionaries:
LOB String/Number pairs
External Service String/Number pairs
Optionally it will receive a customization mappings - one of a two:
Dictionary of String/String pairs - this is name to name customized mapping
Dictionary of Number/Number pair - this is value to value customized mapping
If the customization is omitted, the mapper will try to map in Name-To-Name mode. This means that it will be looking for matched names on both LOB and External Service dictionaries. If a matching name is not found, the mapping process will fail either on initialization on during actual mapping attempt.
Examples:
Default Name-To-Name
LOB values:
Active = 1
Pending = 2
Archived = 3
External Service values:
Submitted = 1
Rejected = 2
Active = 3
Deleted = 4
Pending = 5
Archived = 6
The default mapping will be defined as follows
1 <=> 3
2 <=> 5
3 <=> 6
Same as above with Value-To-Value customization
if a customization was added:
1,1
2,2
3,3
Then the mapper will give it a higher priority than the default Name-By-Name and the resulting mapping will be as follows:
1 <=> 1
2 <=> 2
3 <=> 3
Failing mapping example:
LOB values:
Active = 1
Archived = 2
External Service values:
Active = 1
Submitted = 2
The mapping will fail when trying to map LOB value of Archived.
The situation can be fixed with next customization:
Value-To-Value: 2,2
or
Name-To-Name: Archived,Submitted