Wednesday, May 23, 2018

DCI Implementation in Java

In DCI the class definition "shouldn't contain a single method that is specific to an application’s use case;" i.e. "the domain object is not cluttered by peripheral stuff that it would not contain if not for some use case."
https://dzone.com/articles/dci-architecture-is-visionary

It's suggested that a language that supports DCI should be able to inject behaviors at run-time, which Java can't do as of yet (unless we count Java 8 interface default methods?).

Roles are applied in a run-time context where the roles connect the objects. The reason being is that if the objects (by their static definition i.e. their "class") contain static associations to each and every possible other object it could interact with at run-time, it would quickly proliferate the number of statically defined associations necessary -- hence the rule of thumb to forbid use-case specific behavior within a class.

As for Java, I believe a good trade-off is to either A) admit that the project's software does not pertain to the "domain" at all but that all the objects defined are understood to be within the scope of the project's set of use-cases in that way it's ok to add use-case-specific behavior in the class definition; or B) keep the object's dumb as prescribed by DCI and then let the role's be statically defined and allow the number of the role "classes" to proliferate as needed to accommodate all the use-cases (algorithms plus associations to objects defined in the roles.) (In the latter case, a role is analogous to join table in SQL.)

Regarding option B: obviously DCI assumes domain objects pertain to the larger "domain" of the business and should stay more-or-less static, each maintaining its separate set of invariants. Therefore it might make sense to have use-case specific packages where all the use-case or project specific behaviors and associations among the domain objects are defined in a set of roles residing in their own project/use-case package, which serves as the context in DCI. You could even have a separate Spring XML config file containing all the associations between the use-case-specific classes and the domain objects -- injections go from the latter to the former but not the reverse unless via non-use-case-specific interfaces -- for this reason you would not be able to use @Autowired...@Qualifier because using annotations this way couples the class file definition itself to the use-case-specific algorithm and runs the risk of that proliferation of use-case-specific code in the domain class definition.

I actually lean toward A. My rationale is that the notion of a "domain" sounds nice in theory but in practice it's hard to scope and define because "domains" often overlap -- even if you consider the domain to be the entire enterprise, it may overlap with other enterprises at any given juncture due to mergers or joint ventures. My preference is to actually create project or epic specific models that reside in say a "model" package. These models can be transcripted via tools like ModelMapper or Dozer. Once the project has access to its own possibly trimmed-down model it might make sense to add use-case specific behavior to the model classes since the number of use-cases within a project/epic are likely to be limited in number, so the risk of object-to-object static coupling is minimized. That said, even here I prefer to use "role objects" that contain the algorithms and associations, so you still get DCI in a sense but without worrying about maintaining that nebulous domain concept.

No comments:

Post a Comment