0

I have this object

RelativeFoo{int relativeCode, Origin relativeTo}

And I want to map it to this other object

AbsoluteFoo{int absoluteCode}

In order to do this, I need to use a service whose implementation calls an external API

AbsoluteFoo absFoo = fooProvider.obtainAbsoluteVersionOf(new RelativeFoo(1, Origin.BAR))

In order to be sure that an AbsoluteFoo instance is indeed absolute (meaning, created by the external service), I think that I need to restrict the instantiation of AbsoluteFoo to the fooProvider.

One way of doing it is making obtainAbsoluteVersionOf a static method of AbsoluteFoo (AbsoluteFoo.from(relativeFoo) with AbsoluteFoo having a private constructor) or a instance method of RelativeFoo (relativeFoo.toAbsolute() with AbsoluteFoo having a private constructor and being a inner class of RelativeFoo).

However, since the method obtainAbsoluteVersionOf uses an external API, I can't really make it into an domain object method, can I? I mean, I don't like the idea of injecting dependencies in a domain object, so that's something that I'm not considering as a possibility.

Currently, obtainAbsoluteVersionOf is a domain service that is implemented in the infrastructure layer, and I don't know how to restrict the construction of AbsoluteFoo to this service in this setting.

Any ideas how I could model this better?

Also, I think my question is related to this other one, which wasn't really answered:

https://stackoverflow.com/questions/44776829/external-data-mapping-to-domain

18
  • 4
    This sounds like a horrible idea to me. If you want ever be able to write unit tests which require AbsoluteFoo objects as test data, your tests will depend on this external API. I would usually try to design a system in a way that code which is based on AbsoluteFoo is agnostic of who or what consttructed it. Maybe you can elaborate why you want to design the system this way?
    – Doc Brown
    Jan 11, 2023 at 14:27
  • 1
    Yeah, now I see that. Maybe a better question would be "Should a factory take a service as paramater"? Jan 11, 2023 at 15:42
  • 2
    "What I want is to make sure that the consumer of my domain (application layer) has no option to create an instance of AbsoluteFoo other than calling fooProvider.obtainAbsoluteVersionOf." Yes, but why? You already have an answer. You stated it in the OP! But, you don't like it. Why??? You were assured elsewhere that you don't even need to solve the problem. But, you're still trying to solve it. WHY!??
    – svidgen
    Jan 26, 2023 at 17:53
  • 2
    The details included in your SO post are much more concretely answerable. Though, I disagree with a large portion of the accepted answer there. In the abstract (the OP here), this is nearly impossible to answer. In the concrete (your SO post), the real answer is, "It depends on your risk assessment." If you want to be "sure" X comes from Y, Y should sign it, and your domain entity should explicitly promise it has an X signed by Y. That's not really an "object construction" problem, its a business requirement that should probably be in the domain itself. Maybe as simple as X.isSignedBy(Y)
    – svidgen
    Jan 26, 2023 at 19:41
  • 1
    ... Or perhaps when you create new X(), you need to pass in the signature, the constructor validates it at construction, and the result is an immutable object. On the other hand, if the business just wants to be reasonably sure that X's come from Y. Psh. You own the code. Just don't create X's in the prod codebase apart from calls to Y. Done.
    – svidgen
    Jan 26, 2023 at 19:42

2 Answers 2

1

I think that I need to restrict the instantiation of AbsoluteFoo to the fooProvider.

What you're effectively saying here is that you don't trust your developers to only instantiate AbsoluteFoo for actually absolute values. If that is correct, you've got a way bigger problem on your hands, and that is that you've got developers who don't know what they're doing.

To be fair, some coding guidelines definitely exist to help avoid boneheaded mistakes that are easy to make (e.g. forgetting to update all WET instances, easily breaking magic string values without realizing, ...). There's value to ensuring that developers don't make easy mistakes. I suspect your intentions are along this line.

However, what you're asking for here is a couple steps further than that. You want to design your code so that the compiler vets your business logic to a point of disbelieving any new code unless it conforms to your current idea of what the code should be, in an attempt to use some kind of authorization / access control to not allow developers to do something that the language would allow them to do.

Even if that were a good idea, it would require a truly monumental amount of rule-designing in order to ensure that your developers could only use the code in the way that you've designed it. Which leads me to two subsequent questions:

  • Can the developers make changes to those rules?
  • Who fact-checks the developers who write the fact-checking rules?

This is going to become an infinitely recurring "who watches the watchmen" scenario of distrust, and it's not a healthy development environment.

The solution here is to either train your developers to be more reliable or to only staff reliable developers in the first place, so you don't need to run around constantly second-guessing any code that any of your developers might be writing.

There's a reason code reviews exist. They exist because we have not found a way to automate this kind of review, because this requires a nuanced understanding of both the acceptance criteria and the particular scenario in order to review it. If we were able to relegate this process to an automated ruleset, we'd be doing that already and we wouldn't be manually reviewing code. The existence of the process implies that human eyes are needed to judge the validity of new code.

2
  • 1
    Today I understand and agree with you. Back when I asked this question I was kinda thinking that, in a perfect object oriented system, the code would simulate real world behaviours. Now I understand that it is more about telling a story with objects whose names match real world concepts. Because it is not a simulation, but rather a story, there's no need to create inviolable rules. The only requirement is to have something that, at least from a higher level, happens in a way that is analogous to what I would expect from the respective real world business rule. Makes sense? Mar 8 at 1:56
  • Pretty much. Logic more often than not mirrors real world processes, whose design often stems from real world constraints that might not apply in the digital realm, but they are maintained nonetheless.
    – Flater
    Mar 8 at 2:56
0

Design patterns suitable to model AbsoluteFoo instantiation are builder design pattern and optionally, depending on where AbsoluteFoo has to be instantiated, factory design pattern:

        Factory design pattern, specifically abstract factory design pattern, for polymorphic object creation, encapsulates object creation code, thus following single responsibility principle (SRP) of Clean Architecture, and reduces code duplication, thus following don't repeat yourself (DRY) principle of The Pragmatic Programmer.

        Builder design pattern for sequential object creation, encapsulates object creation code, thus following SRP.

The layer handling external services invocation converts external responses to internally known representations returning to calling layers application specific entities, or domain objects, that can be created using instances of entities factories or entities builders from the layer handling the application's data model. Summed up for the actual scenario fooProvider should hold a reference of AbsoluteFoo factory or builder.

Update for comments:
Reduced accessibility to instantiation could be achieved, using access modifiers and inheritance, through package by feature.

1
  • Hi! Thank you for answering. About the answer, how could I restrict the construction to the fooProvider? I can see that it will need a reference of AbsoluteFoo factory or builder, but how can I make sure that only the fooProvider service can create instances of AbsoluteFoo? Should I do this at all? My intention is to avoid developers creating instances of AbsoluteFoo by means other than the fooProvider (this service could be mocked for the tests, so the test part is ok). Jan 25, 2023 at 13:26

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.