I have an object that contains several dozens of properties. A portion of property values come from Service A, others from Service B, and the remaining from Service C. This object is a flat model that is used to be serialized as a request REST call to another external service.
If I define the service APIs to accept the same object, then I can write something like:
Object: MyModel
Service A's API: MyModel getAProperties(MyModel mm)
Service B's API: MyModel getBProperties(MyModel mm)
Service C's API: MyModel getCProperties(MyModel mm)
Each API returns the same reference that is passed to its signature (see above):
MyModel mm = new MyModel()
serviceA.getAProperties(mm);
ServiceB.getBProperties(mm);
ServiceC.getCProperties(mm);
I am trying to see if there is an already established design pattern that provides a more elegant solution to this.
-
1What are tradeoffs in your scenario, what kind of elegancy you are seeking?Fabio– Fabio12/09/2020 06:03:25Commented Dec 9, 2020 at 6:03
-
Well, you have what we know as DTO and it's intended to be implemented exactly as you described. When you ask for a "more elegant" way I'm afraid of you asking for a more sophisticated way. Sure there's, I can come up with a handful, but all of them increase complexity with no gain in exchange. What's the real problem with your current solution? The "real" one, not the one you "think" you have. The difference between the "real" and the "non-real" is that the first cause real issues or even prevents from progressing in the development. The second doesn't. Yet.Laiv– Laiv12/09/2020 14:19:23Commented Dec 9, 2020 at 14:19
-
Relevant: Design pattern for aggregating/gathering from different sourcesJohn Wu– John Wu12/09/2020 19:21:37Commented Dec 9, 2020 at 19:21
-
Either you are populating a composite entity (an entity itself composed of multiple entities) or your (RESTful?) APIs are returning partial entities. Which case are you trying to solve for?Jason Weber– Jason Weber12/10/2020 01:21:51Commented Dec 10, 2020 at 1:21
3 Answers 3
I'm afraid there's no easy solution, if you want to stick to object-oriented design. You'll have to redesign the services and the object.
Basically, if there is a "thing" that is important enough to be published to a client, then that thing should exist as an object in the first place. And that object should be able to serialize itself to the client.
Gathering raw data from different sources and putting in into a data structure is about as far from object-orientation as one could get. Although it is surprisingly common.
-
This might not be object oriented, but MyObject in this case is already a POCO serialized for transport across the network. There is nothing wrong with procedural code at the boundaries of an application, which is why you see this so often.Greg Burghardt– Greg Burghardt12/10/2020 02:47:58Commented Dec 10, 2020 at 2:47
-
1"There is nothing wrong with procedural code at the boundaries of an application...". Sure, just don't call your code object-oriented then. The same way I wouldn't call code with side-effects functional, regardless of what excuse is presented. I'm not saying anything is wrong, I would just like the terms to mean something. Especially in this case, where the solution could be as easy as slapping a
toJson()
method on some object instead of creating a data structure.Robert Bräutigam– Robert Bräutigam12/10/2020 07:50:08Commented Dec 10, 2020 at 7:50
If you go clean architecture way:
- implement entities/objects coming from services as domain entities, likely DTO style, i.e. POCO;
- complex class accepts the entities in constructor;
- use case (app layer) fetches the required param objects, creates a complex object and probably does something with it or just returns as a result.
Gathering data from different sources and putting it together in a useful object-- that is bread and butter programming. There is no set pattern because it is so common and is done in so many different ways.
In the case where you have several external services, typically those would be wrapped in repository classes, each of which is for a single service. Your application would then have a service class of its own which uses the repositories to obtain the data and returns it in the format needed.
Your controller might look like this:
class MyController
{
protected readonly IMyService _service; //injected
public MyController(IMyService service) => _service = service;
public async IActionResult MyAction()
{
var model = _service.GetConsolidatedData();
return View(model);
}
}
The service would accept the 3 repositories as injected dependencies, make the calls, and coordinate the data. That is the purpose of the service layer in this case.
class MyService : IMyService
{
//All of these are injected
protected readonly IRepoA _repoA;
protected readonly IRepoB _repoB;
protected readonly IRepoC _repoC;
public MySerivce(IRepoA repoA, IRepoB repoB, IRepoC repoC)
{
_repoA = repoA;
_repoB = repoB;
_repoC = repoC;
}
public MyModel GetCosolidatedData()
{
var a = _repoA.GetData();
var b = _repoB.GetData();
var c = _repoC.GetData();
return new MyModel
{
Data1 = a.SomeData,
Data2 = b.SomeOtherData,
Data3 = c.YetMoreData
};
}
}
Explore related questions
See similar questions with these tags.