lucianonooijen

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!

Showing Posts 1 to 9

LostKobrakai

LostKobrakai

Behaviours is erlangs/elixirs way of having interfaces. Take for example a genserver. It expects a callback module, which implements the :genserver behaviour. When such a module is given to e.g. GenServer.start_link everything will be called correctly by the :genserver module 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

lucianonooijen OP

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

LostKobrakai

mod = MyApp.BehaviourImplementingModule
MyApp.func(mod)

There’s no type safety on the beam. A module as typespec is module(), which is a alias for atom(). 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

lucianonooijen OP

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.func method be typed?

What would the MyApp.func typespec look like to have compile-time checks on the mod (meaning MyApp.BehaviourImplementingModule which implemented MyApp.SomeInteractorBehaviour) passed to MyApp.func, to check if all MyApp.func’s calls to mod would be “valid”?

Thanks a lot in advance!

LostKobrakai

LostKobrakai

You can’t. I just answered this on slack a few hours ago. I’ll just paste this here:

  • there’s only module() as built-in type or
defmodule MyBehaviour do
@type t :: module()
end
  • and MyBehaviour.t to at least be a bit more concrete for humans

  • also 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

lucianonooijen OP

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

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

lucianonooijen OP

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

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.

— All posts loaded —

Where Next? Top

Trending in Questions Top

RSP87
I’m working on a project that simulates the bumbl example in the programming phoenix book. It acts almost like an email client. We have a...
New
nseaSeb
Hello, I know there is an approach for handling lists that allows for optimized traversal, but I can’t recall the specific method (somet...
New
RemyXRenard
I’m seeing that a list inside a Kino.DataTable will be interpreted as a charlist, even if the Kino.configure() is set to charlists: :as_l...
New
velrest
So my question is quite simple and i have found no conclusive answer on forum, google or AI. Should we use :erlang.float for Integer to ...
New
samoloth
Hi, I’ve just set up an application with ash_authentication. There is only magic link strategy for now, so there is no confirmation add o...
New
brecabral
Documentation While reading the Scoped Routes section, I noticed that the documentation currently refers to a problem without explainin...
New
FlyingNoodle
If a change or preparation module uses Ash.Changeset.get_argument/2 or Ash.Query.get_argument/2 (or any of the other get_argument functio...
New

Other Trending Topics Top

mudasobwa
I am happy to introduce the very α version of the new programming language compiled to BEAM. Welcome Cure. It has literally three kille...
New
marciok
Hi there! We created Gust: A task orchestrator inspired by Airflow. For those who have never heard about Aiflow, it’s a Python-based wor...
New
jimsynz
Beam Bots (or just BB for short) is a framework for building fault-tolerant robotics applications in Elixir using familiar OTP patterns. ...
New
Dmk
Xamal is a deployment tool for Elixir apps that deploys native releases to bare metal servers over SSH. It’s a port of GitHub - basecamp/...
New
netoum
Corex is an accessible, unstyled UI component library for Phoenix that integrates Zag.js state machines using Vanilla JavaScript and Live...
New
webofbits
With AI doing more of the implementation work, I’ve been wondering how much coding I should deliberately keep doing myself. My main conc...
#ai
New

We're in Beta

About us Mission Statement

Options

Thread Display Mode




Thread Preview

Skip Thread Previews