lucianonooijen
At the moment I am creating a plan to integrate Elixir at enterprise scale for several clients of the company I work for.
Part of this integration would mean the application should comply with the guidelines as outlined in Uncle Bob’s Clean Architecture. (A tldr version can be found here: Clean Coder Blog)
The part I am currently stuggling with is using Interactors in Elixir, without the use of any libraries.
How Interactors are usually implemented is by creating an Interactor interface (with all available functions/methods). The business-logic core expects an object where this Interactor is implemented, so the business-logic core can be fully agnostic of data sources and such.
An example of an interactor based design in Go can be found here: go-realworld-clean/uc/userGet.go at master · err0r500/go-realworld-clean · GitHub
As far as I know there is no easy way to define an interface in Elixir, the way you’d usually do in languages like Go, Typescript, Java or even Scala. I was not able to find any good examples where this kind of architecture is implemented in Elixir.
Using typespecs/behaviours/protocols seems to make the code become quite messy if the Interactors grow in size, so I am looking for a way to implement this architecture while keeping the code clean and easy to maintain.
Thanks a lot in advance!
Trending in Questions
Other Trending Topics
Categories:
Sub Categories:
Forums
Popular Tags
- #ecto
- #liveview
- #troubleshooting
- #learning-elixir
- #library
- #deployment
- #erlang
- #testing
- #genserver
- #mix
- #absinthe
- #remote-other
- #otp
- #plug
- #how-to-question
- #macros
- #postgres
- #elixirconf
- #channels
- #exunit
- #discussion
- #code-sync
- #podcasts
- #javascript
- #onsite
- #dialyzer
- #docker
- #authentication
- #umbrella
- #full-time-contract
- #podcasts-by-brainlid
- #ecto-query
- #ai
- #elixirconf-us
- #blog-post
- #elixir-ls
- #phoenix_html
- #iex
- #graphql
- #genstage
- #websockets
- #supervisor
- #advent-of-code
- #distillery
- #processes
- #api
- #forms
- #hex
- #security
- #metaprogramming










Showing Posts 1 to 9- Show Best Posts
- Show All (oldest first)
- Show All (newest first)
LostKobrakai
Behaviours is erlangs/elixirs way of having interfaces. Take for example a genserver. It expects a callback module, which implements the
:genserverbehaviour. When such a module is given to e.g.GenServer.start_linkeverything will be called correctly by the:genservermodule without it needing to know about the specific callback module in advance. This is similar to how in oop you might expect an object, which implements a certain interface.lucianonooijen
Thanks for the response!
How we currently implement the interactors is by building an object based on an interface in an outside layer, then pass that object as an argument to the function in the core. A screenshot of the dependency flow can be found here: https://imgur.com/a/MEYPGDN
I see how using behaviors can be used to create modules like you would implement an interface in OOP. What is still a bit blurry to me is how a module (that implements a behaviour) can be injected as an argument, and how the types can be specified (for type safety).
How could this be achieved in (vanilla) Elixir? Thanks!
LostKobrakai
There’s no type safety on the beam. A module as typespec is
module(), which is a alias foratom(). It’s basically working by ducktyping. If the correct callbacks are defined on the module of the supplied name and they accept/return the correct values everything is fine. If not it’ll fail at some time.lucianonooijen
Thanks for your prompt reply.
Seems pretty clear for the most part.
The part still confusing me is how the module methods and their types could be checked at compile time.
For example, how could the
MyApp.funcmethod be typed?What would the
MyApp.functypespec look like to have compile-time checks on themod(meaningMyApp.BehaviourImplementingModulewhich implementedMyApp.SomeInteractorBehaviour) passed toMyApp.func, to check if allMyApp.func’s calls tomodwould be “valid”?Thanks a lot in advance!
LostKobrakai
You can’t. I just answered this on slack a few hours ago. I’ll just paste this here:
module()as built-in type orand
MyBehaviour.tto at least be a bit more concrete for humansalso it’s not really a good fit for dialyzer, which will only error if it can be certain about a failure. Modules can be loaded at runtime, so there’s no way for dialyzer to check callbacks in advance
lucianonooijen
Thanks for your reply. Trying to use Elixir this way is probably using the language in a way it’s not built for.
As the goal is to achieve a type-safe layered architecture (based on Uncle Bob’s Clean Architecture philosophy) and there are probably multiple ways to achieve this.
The thought of defining structs and passing these as arguments passed through my mind, but it seems like there is no way to define methods and method types in structs.
Do you have any ideas on how to achieve this kind of architecture in Elixir without any libraries?
I should also mention I have mostly been using Typescript for the past few years for back-end projects, and I’ve just recently started working with Elixir, so my knowledge about the language and the underlying philosophy is not very extensive.
Thanks!
LostKobrakai
You seems to be stuck at “type safety” which just doesn’t exist on the beam. There is no type safety. But GenServers work solely on the idea of a callback module being passed in and it’s basically the backbone behind most of everything running on the beam and it works well even without the type safety. You can have tests ensuring that a certain module conforms to a certain behaviour. You can use whitelists of some kind to allow just tested modules as inputs. If you really want to prevent someone passing in a wrong module there are ways, but not via types. I’d rather suggest making your app handle failure properly and weed out those bugs when they actually happen.
lucianonooijen
Thanks for the reply. I was under the impression Elixir did have compile-time checks, but that was probably due to me being an Elixir-rookie.
Do you have any further tips on achieving a layered architecture within Elixir, or enterprise software design with Elixir in general, as it seems like the amount of resources on this topic seems quite limited.
Again, thanks a lot for your help so far!
LostKobrakai
There’s dialyzer, which can do type checks, but it works considerably different to e.g. typescript. Bu even typescript would have problems with checking if a certain class implements an interface if the class could be created at runtime out of nowhere.