Replies: 2 comments
-
One consequence of this I noticed is that there is a chance of having the same name appear twice in the package tree, which feels awkward.
Notice we have One way to work around this could be to update the domain subpackage name to indicate it's a subdomain, e.g.:
Using plural for naming domain slices (i.e. |
Beta Was this translation helpful? Give feedback.
-
I think this is a good illustration of when using packages for grouping can be useful. Anyway, I have a couple of points I want to make. I don't think your example violates the second rule, at least the way I interpret it. I see architecture as fractal - what is detail at a higher level can become a concept at a lower level. In Another point I want to make is that I don't think the readability of the package hierarchy is the only, or even the most important purpose of the three rules. I think another purpose is to maximize cohesion and minimize coupling between packages, making large scale refactoring, or even decomposing the codebase easier. Finally, just to nitpick, two high level packages, where one depends on the other to plug together the domain model objects, is not srictly inevitable. I wouldn't recommend this, but technically you could move out all top level domain packages into separate libs, import them as compile time dependencies and make the whole application just the |
Beta Was this translation helpful? Give feedback.
-
Let's say we're building a new app and we'd like to follow these packaging rules:
We start with the following packages:
com.company.domain
(for the domain model)com.company.app
(forMain
class and app configuration that ties everything together)An implication of the "Packages should never depdend on subpackages" rule is that having two high-level packages is inevitable (because we need to instantiate/plug-together the domain model objects, and having that code anywhere in the same package subtree that holds the domain objects would break the rule).
Now let's assume the domain of the app is that of managing various types of reference data, where different types of reference data have few or no dependencies at all. Let's say the types of reference data are:
In such case it would seem natural to organize the domain model into 3 subpackages - one for each type of reference data:
com.company.domain.airport
com.company.domain.plane
com.company.domain.weather_data
It could be argued that the subpackages of
com.company.domain
break the "Sub-packages should not introduce new concepts, just more details" rule, as:com.company.domain
package has no classes inside of it (i.e. it defines no highest-level concepts)com.company.domain.*
subpackages introduce new concepts, rather than refining higher-level onesHowever, it seems that using subpackages solely for logical grouping has value, and could be useful at different levels of the package hierarchy.
In addition to the example above, the
com.company
subpackages (domain
andapp
) are also there just for the logical grouping they provide.But subpackages for logical grouping can be useful at a lower level as well.
Let's consider another example. Let's say we're modeling local codes defining the requirements for electrical installations (here NEC stands for "National Electrical Code" and CEC stands for "California Electrical Code"; every few years a new version of code comes out that changes some requirements). The following structure would seem to be in line with the 3 packaging rules:
Yet if we also have other concepts in
com.company.domain
package, implementation of which is placed in related subpackages, then our electrical code implementation subpackages might get lost among implementations of other concepts. A simple solution is to useelectrical_code
subpackage for logical grouping ofnec2020
,nec2017
, andcec2019
subpackages.This feels like an improvement with seemingly no downsides.
Similarly, if over the years more editions of NEC and CEC codes were added, we may want to group them together into
nec
andcec
subpackages as well:This would also seem like an improvement, assuming there were e.g. 3+ versions of each type of electrical code to warrant the grouping.
This suggests we can use one or multiple levels of subpackages to express just the logical grouping at any level of the hierarchy.
At first I thought that the consequence of this was that if we are considering package
P
and its subpackages, we can have only two distinct cases (with no overlap):P
defines some concepts (i.e. has some classes or interfaces), and subpackages provide implementations/additional details at a lower-level of abstraction,P
is empty and only provides logical grouping for the contained subpackagesHowever, this is not the case.
If we modify the example above by adding
SomeNecCalculationCommonToAllEditions
class that would be used by bothNec2020
andNec2017
, thencom.company.domain.electrical_code
package would both serve as logical grouping for different NEC versions, AND also introduce a new higher-level concept of some calculation common to all versions of NEC:So the conclusions would seem to be that:
com.company.domain.ElectricalCode
andcom.company.electrical_code.nec._2020.Nec2020
). In other words, implementation does not have to reside in first-level subpackage, it can be placed at a deeper level if needed.Beta Was this translation helpful? Give feedback.
All reactions