sasa1977 / boundary

Manage and restrain cross-module dependencies in Elixir projects
MIT License
818 stars 21 forks source link

Ignoring violations #53

Closed PragTob closed 1 year ago

PragTob commented 1 year ago

👋

Hey there and first as always thanks for all your amazing work 💚 🙌

I tried slapping on boundary to the good ol big application and well, as you can imagine it unveils all kinds of sins 🙈

Overall, for adoption any mechanism to ignore specific kinds of warnings would be helpful (let's say from one domain to another). That way, it could already run in CI (avoiding new errors sneaking in). I think that'd be helpful - so far I'm just adding them as dependencies (as Web as dependency for non web) and then end up with dependency cycle found errors, which is fair - so a quick fix (for me) would be being able to ignore these/not run this check, giving us time to adopt and later fix the errors.

Regarding cycles, I have another problem where I wonder if there is a better solution or if it also requires ignores:

We've been experimenting with pulling out applications "inside" the application first. So, we first completely separate it while it's still technically the same application. Say A is the application, B is what we want to pull out. Now of course the Application A should only depend on B so: A ---> B.

However, we're in a domain where B also needs to update A after a while about events that happened. Later this might be a webhook or publishing an event, if we fully extracted it. So far, the prototype allowed B to call a specific A.Callbacks module. As is, this will always be a dependency cycle (that we are aware of), but without ignoring cycles/dependencies I don't see a way to solve this right now 🤔

Again, thank you a lot for all your great work! 💚

Have a bunny:

IMG_20191006_095842

sasa1977 commented 1 year ago

For ignoring boundary warnings, there's the check option where you can choose to ignore any calls to and/or from a specific boundary. See here for details. Would that suffice?

When it comes to cycles, they could definitely be resolved without ignoring the warning. A few options that come to mind:

  1. B specifies the contract (e.g. behaviour) for firing notifications, while A passes the implementation via an argument.
  2. A subscribes to async (future) notifications from B via a registry which is implemented in B and exposed via some public API of B.
  3. Extract the notification mechanism to C which is a dependency of both A and B.

Without knowing the exact details I can't provide more specific recommendations, but speaking generally these all look better to me than having cycles.

PragTob commented 1 year ago

@sasa1977 I am very sorry, that has completely skipped my reading of the docs for whatever reason, probably that I'm lazy... I'll self reflect and see if maybe I can make it a bit more clearer for people like me 🙈

Thanks for your recommendation on the problem as well. I believe all of them require some form of storing/saving/configuring a module to call (implementation passed in 1., the way that A subscribes in the registry, the module/pid whatever C needs to call to tell A, as C can't depend on A) and while it's no direct dependency then, it indirectly is (it still is called/depended on, just not hard-wired in code). Or I'm missunderstanding.

Anyhow, shouldn't be discussing these things on an issue tracker - thanks a lot for your great work and generous help! I'll meditate on these and may take it back to elixirforum or first go through it with my colleagues which may have also saved me missing the check option 😅

IMG_20210220_102234_Bokeh

sasa1977 commented 1 year ago

I am very sorry, that has completely skipped my reading of the docs for whatever reason, probably that I'm lazy.

No worries, there's a bunch of stuff in there. I'll think about making this feature more obvious, currently it seems a bit buried.

I believe all of them require some form of storing/saving/configuring a module to call (implementation passed in 1., the way that A subscribes in the registry, the module/pid whatever C needs to call to tell A, as C can't depend on A)

Yeah, you need to somehow provide the implementation. I've described one example of such approach here, in the "Activation mail" section.

In the third approach you might not need to inject the dep if you move required parts from both A and B into C. But this might not give you a clear separation of concerns (e.g. you might end up mixing interface and the core concerns).

while it's no direct dependency then, it indirectly is (it still is called/depended on, just not hard-wired in code)

This is a runtime dependency, not a compile-time one, so it's allowed by boundary, and it would also work in umbrella projects.

Anyhow, shouldn't be discussing these things on an issue tracker - thanks a lot for your great work and generous help! I'll meditate on these and may take it back to elixirforum

If you're discussing on Elixir forum, feel free to ping me in your post. Otherwise, should I enable discussions here? In principle I'm ok with that, but at the same time I'm a bit worried that in most people wouldn't notice them here.

PragTob commented 1 year ago

Yeah I think people won't notice here :)

I just feel like, the topic is more appropriate for a wider audience and OSS maintainers already give a lot to the community. Expecting these deep discussions on an issue tracker is a bit much and not as well searchable so I think elixirforum/mailing list/something is a better place. And, eventually putting it all together in a nice blog post would make a nice topic I think :)

axelson commented 1 year ago

I'll chime in real quick to say that a blog post about applying boundary to a larger codebase would be very much appreciated! By one person at least :laughing: