Fl4m3Ph03n1x
How do you manage umbrella apps with inner dependencies?
Background
I have an umbrella application that has several apps inside. These apps communicate and depend on each other.
So, when I am testing, because I am using mocks, I need to mock their interfaces. Now if you know about mocks or if you have read Jose Valim’s opinion on them (Mocks and explicit contracts « Plataformatec Blog) you know this brings an issue:
- Using Mocks means I can deviate from the real implementation.
App Structure
Imagine I have an umbrella project with 2 apps:
- cli (a command line interface)
- manager (where all the logic is)
Umbrella Project structure
.
├── README.md
├── apps
│ ├── cli
│ └── manager
├── config
│ └── config.exs
├── mix.exs
└── mix.lock
Now, every time I change the manager’s Public API, I have to update the interface on cli, otherwise my tests in cli will pass even though nothing will then work:
cli app stucture
.
├── README.md
├── lib
│ ├── cli.ex
│ └── manager.ex # interface for the manager umbrella app
├── mix.exs
└── test
├── cli_test.exs
├── support
└── test_helper.exs
Problems
So, following this pattern, I have a few issues:
- every time create a new
@typein themanager, I have to update all the interfaces that use it, so the projects usingmanageras a dependency can have access to the new types I created. - every time I change
manager’s public API, I have to update the projects that use it as a dependency - I have a
manager.exfile in mycliapp, that is just an interface for themanagerapp. This is confusing, as people reading my code will not intuitively understand this is just an interface so i can create a mock that obeys it (mocks as nouns :D)
Possible solutions?
Given these issues I have considered the following option:
- create a new app, called
interfaces, where I define the interfaces of all umbrella apps.
This would have the following benefits:
- Changes to the Public API of any app would always be reflected there.
- All dialyzer
@types would be there as well - Any project using the Interface app as a dependency would have immediate access to the most updated interfaces and type specs.
- I would get rid of the
manager.exfile because I would import it from theinterfaceapp
It would however have the following drawbacks:
- Any umbrella app would be forced to have 2 dependencies: the
interfaceapp, and the app they want to use (in this case,cliwould have to import bothinterfaceandmanager) - They would have access to all interfaces and type specs, even if not needed
- I would still have to change things in two places: the
interfaceapp, and the app that implements the interface.
So I am not really sure on how to deal with this issue.
How do you guys deal with this?
Most Liked
LostKobrakai
Yeah, but :cli doesn’t need to be able to interact with multiple backends I imagine. :manager is the only one it interacts with. So no decouling in that direction.
So what? To me this is not a problem in itself. If there’s something in manager, which makes testing hard, this might change. But for that fact of the part making it hard, not because I want to not have end to end tests.
egze
I guess. Never thought of it in terms of patterns.
I took this approach from this blog post https://medium.com/onfido-tech/the-not-so-magic-tricks-of-testing-in-elixir-2-2-acdd0368572b
See part about “Don’t mock what you don’t own”. In the context of app, we don’t own api_client, so we make a wrapper for it.
LostKobrakai
I guess we already had this conversation, but I personally would not mock internal dependencies in the first place. But manager.ex is one file in :cli, which belongs to :cli and is built to its needs. What exactly would you be copying from :manager and why?
Popular in Discussions
Other popular topics
Categories:
Sub Categories:
Forums
Popular Tags
- #ecto
- #liveview
- #troubleshooting
- #learning-elixir
- #deployment
- #library
- #erlang
- #testing
- #genserver
- #mix
- #absinthe
- #remote-other
- #otp
- #plug
- #how-to-question
- #macros
- #postgres
- #channels
- #elixirconf
- #exunit
- #discussion
- #code-sync
- #javascript
- #podcasts
- #onsite
- #dialyzer
- #docker
- #authentication
- #umbrella
- #full-time-contract
- #podcasts-by-brainlid
- #ecto-query
- #elixir-ls
- #phoenix_html
- #iex
- #blog-post
- #graphql
- #genstage
- #ai
- #websockets
- #supervisor
- #advent-of-code
- #elixirconf-us
- #distillery
- #processes
- #forms
- #api
- #metaprogramming
- #security
- #performance









