-
Notifications
You must be signed in to change notification settings - Fork 15
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Should we enforce the Dependency Inversion Principle (DIP)? #75
Comments
Nice issue! Could you please add examples for the suggestions so we can discuss it better. Also From the dependencies see it like this:
flowchart LR;
subgraph npm
models-->zod;
utils-->simple-git;
cli-->yargs;
cli-->chalk;
cli-->uicli;
plugin-*-->eslint;
plugin-*-->lighthouse;
subgraph code-pushup
utils-->models;
utils-->|X|cli;
cli-->models;
cli-->core;
core-->models;
core-->utils;
plugin-*-->utils;
plugin-*-->models;
end
subgraph integrator
gh-action-->models;
gh-action-->core;
nx-validators-plugin-->models;
nx-validators-plugin-->core;
end
end
click models "https://github.com/flowup/quality-metrics-cli/tree/main/packages/models" "view source"
WDYS? Regarding the todos:
Here I would say that Plugins import
Could you please give some examples here |
I agree. I might also see it as a dependency in models but that is just to simplify typing.
If the plugins import Yargs they will implement their own abstraction and not reuse the ones in models. We probably want to have the cli handle the prompts, the param parsing and the logging. The plugins can use these through the utils. For example the logger can be a singleton service streaming values. If it’s exposed through utils but instantiated in cli and called in core and plugins then we can handle its behavior however we want. If we want to only execute on verbose or have different levels or logging that acceptable. If we want the logs to be persisted (great for testing and debugging) then the we can implement that in this logging service. |
Fully agree 🙂 The Mermaid diagram illustrates it very well, I think 👍
@ChristopherPHolder Not sure what you mean by "abstractions" here exactly. The |
Motivation
This pattern promotes decoupling, making the system easier to maintain, scale, and modify. It also facilitates unit testing since you can mock the abstractions.
Implementation Overview
We can achieve this be making sure the
Core Module
and theCLI
both depend on the abstractions in theModels Module
. This means we would move the abstraction there and that module will actually dictate the behavior of the system.(In the diagram you can see the the dependency between CLI and Core is a doted line. This is because the core module should implement the abstraction in Models and therefore CLI actually depends on Models and Core could be replace with anything that satisfies the abstractions in models)
By making sure the abstraction are in the
Models Module
and that both theCore Module
and theCLI
depend on these abstraction we essentially inverting the dependency such that higher-level and lower-level modules both depend on an abstraction rather than the higher-level module depending on the details of a lower-level module.Because the
Core Module
handles implementing the abstraction from theModels Module
, theCLI
does not require depending on theUtils Module
. Therefore I suggest we enforce module boundaries on theCLI
so that it can only depend onCore
andModels
.This would also mean that the
Plugins Modules
do not require theCore Module
, as all abstraction needed to create a plugin are located in theModels Module
and all the function that would help the plugin authors create these plugins are located in theUtils Module
.As part of this i would also suggest we enforce module bounties so that the
Plugins Modules
can only depend onUtils and Modules
. Potentially in the future we can also have testing utils and the boundaries can be extended to include that module.Because
Utils
depends onModels
and and core implements the logic related to theCLI
execution. We should be very mindful of what can be imported in the module. For example it should not importyargs
as that should belong in theCore Module
, nor should it import anything that is specific to a plugin, likelighhouse
or any eslint internal.TODOS
Enforce Module Boundaries with NX
Restrict imports in Utils and Plugins
Move an create abstraction in Models
Related to: Move core logic into new "core" package #73
The text was updated successfully, but these errors were encountered: