kentan88

kentan88

Does my code/directory structure makes any sense?

Hi all, I’m relative new to Elixir/Phoenix here.

I’m trying to structure my code properly for ease of development. I’m also trying avoid bloated contexts, applying SRP to the best of my ability. Would really appreciate if I can get some feedback on my github repo here . Am I doing too much, too little or does it not make sense at all? Thanks in advance!

Most Liked

yordisprieto

yordisprieto

I see you are trying to follow Uncle Bob architecture. So I could share some personal experiences since I did most of the things you did in that project (all of them actually).

Also, worth to mention that this is an art and there are many ways, there is no right way to do it.

I will be using this package as an example, https://github.com/straw-hat-team/straw_hat_map

After doing what you did, I decided to move to DDD folder structure. The issue that your folder structure has, in my opinion, is that represents a programmer driven structure.

Entities

Originally I had it the way you have it right now, a mega folder (it will get big) with most of my schemas (entities), most of the time it was a pain having to go inside another folder all the time when the schema is only used on the a specific context (most of the time this will be the case if you do the DDD right).

What would happen when you have two schemas that would make sense to have the same name, but they are different depending on the context?

For example (for the sake of making a point), account from Accounts context and account from Sales context.

How will you structure your schemas folder now? You will keep creating folders or files (it will get big)

I am trying to make a more DDD approach to my folder structure. I no longer have any folder called schemas/entities/models,`.

I put a file in singular module and file name closer to the context that will be used, so you don’t have to jump between many folders.

Reduce the entropy of the files and folders.

Example: https://github.com/straw-hat-team/straw_hat_map/tree/master/lib/straw_hat_map/addresses

Queries

I would say, wait until you need actually to have a file with all the queries.

Initially, once again, I did what you did, but I had to go inside queries folder or file and to have to go back and forward between files that most likely will be touch at the same when you are working in that specific context.

I removed the concept of having queries inside a folder/file.

I put the queries on my context module and unless the file becomes problematic to manage I will no move it.

Most of those functions will become simply private functions on your context module; but once again, one less file that you need to open and paid attention to (wait until you have an issue).

Reduce the entropy and file management.

Worth to mention that most of those queries will be import in use cases that belong to the same context, so you will have to keep importing the same query module for all the services from a specific context.

Services

Once again since I did the same, I removed this concept of service.

My service is my context module, or I would call the module that implements the use cases https://github.com/straw-hat-team/straw_hat_map/blob/2baa9fc69e7d69b51239a86180d694df5b202a16/mix.exs#L100-L108 you see that those modules will perform the business use cases.

I don’t need to open many files to implement or work on use cases. Especially that becomes more problematic as the use cases start sharing pieces of code.

With your solution, the issue starts with the fact that all your modules will have a process function or some function that it will implement the use case because your module name is the name of the use case.

In my case, the functions are the name of the use case, and I don’t need to create that many files.

Example: https://github.com/straw-hat-team/straw_hat_map/blob/master/lib/straw_hat_map/world/countries/countries.ex

All the public functions (or at least that will appear on ex_doc documentation are most likely use cases).

The ugly comes when you need multiple use cases in one use cases.

Since you went for the route of using the module name as the use case namespacing, now you will need to import modules when you need to have access to the use case.

Even when you are in the same context, and if you decide to create some function that could is shared between use cases, the mess start becoming even more prominent.

In the approach I ended up with, the use cases for specific context are closer to each other, and it is easier to reuse.

Reduce the entropy between your shared functions.

Module Names vs Folder Structure

Worth to mention that the name of my modules does not follow the folder structure.

The source code is optimized for readability and usability than actually finding files by module names.

You can always do a generic search for function module names, at least you don’t have long names on your modules just because you want to follow the folder structure.

Also module name driven by folder structure makes is harder to move files around since you will have to rename everything.

I tend to create a folder for creating some context and encapsulate files that would most likely live together.

TD; TR

Reduce the entropy of your files and code, the higher the entropy the harder becomes to maintain in, the long term (I love to open Erlang source code because of this)

Use your module as the namespace for your context and use functions as your use cases.

Do not use folder structure as your module name.

Separate files and module based on contexts, no responsibilities.

Clean code is not about conventions.

Conclusion

I hope you find my comment useful.

As I said, I did all the things you propose here and to be honest, I hated myself doing it.

Because of my background from Rails and the habit of convention I had to expirience the pain of learning about this.

I am pleased with the Straw Hat repositories structure, it is nice for consumers of the packages and maintaining it is easier since you don’t need to have that many files open, and the entropy of the files are minimal.

I would say that the only downside is that the folder structure does not represent the module names so you can’t simply assume where the module name locates the file, but you can always search for it, the trade-off.

Still learning and improving, just keep DDD and entropy in mind.

But, no there is nothing wrong with your setup, but subjective speaking I wouldn’t do it since I did exactly what you did and I moved away from it.

joaoevangelista

joaoevangelista

Yes it does! Since most of the time you’ll be sharing schemas across contexts its a good place you put them. Just remove the _ from the folders, so they all can conform on a notation :slight_smile:

yordisprieto

yordisprieto

I need to write another answer here. So much has changed for me that I wouldn’t suggest doing that, except if you don’t have any better strategy. :sweat_smile:

Where Next?

Popular in Questions Top

bsollish-terakeet
Credo is smart enough to check for (something like) this: assert length(the_list) == 0 with this response: Checking if an enum is empt...
New
Werner
Hi, I’m using Ubuntu 18.04 and after updating to OTP-24.0 yesterday i have this warning when I run “mix local.hex”: 14:57:30.512 [warn] ...
New
quazar
How to set Jason to encode all fields in ecto schema, I don’t care about security and implementing only is taking long list of attributes...
New
nsuchy
Hi. I’ve noticed that Windows Powershell has it’s own IEX command and you cannot access Elixir’s IEX due to the conflict. This isn’t a cr...
New
chensan
I have a User schema with a :from_id field set to type :string: defmodule TweetBot.Repo.Migrations.CreateUsers do use Ecto.Migration ...
New
gonzofish
I’m currently trying to understand how to join three tables using Ecto. All the examples I’ve seen use 2, so maybe I’m just missing somet...
New
johnnyicon
Hi all, I've just started learning Elixir and Phoenix Framework, so please pardon my n00bness at this stage. I'm trying to use Postg...
New
ovidiubadita
Hey all, I discovered Elixir and I love it. I always wanted to learn a functional programming and I intended to go for Haskell, but afte...
New
Codball
Mix format works fine if run from the cmd. I’ve followed this to facilitate the implementation into VSC which involves downloading an ext...
New
vrod
I am using the Starship cross-shell prompt – it seems pretty nice, but I get some errors: [WARN] - (starship::utils): Executing command ...
New

Other popular topics Top

Brian
What is the proper way to load a module from a file in to IEX? In the python world, doing something like this pretty standard: from ....
New
chrismccord
Phoenix 1.4.0 released Phoenix 1.4 is out! This release ships with exciting new features, most notably with HTTP2 support, improved deve...
688 30048 115
New
Tee
can someone please explain to me how Enum.reduce works with maps
New
bsollish-terakeet
Credo is smart enough to check for (something like) this: assert length(the_list) == 0 with this response: Checking if an enum is empt...
New
KronicDeth
Elixir plugin for JetBrain’s IntelliJ Platform (including Rubymine) This is a plugin that adds support for Elixir to JetBrains IntelliJ...
289 35421 110
New
lk-geimfari
What is most correct way to open, read and parse JSON file with poison? For example if we have example.json file in root of some projec...
New
hariharasudhan94
I would like to know what is the best IDE for elixir development?
New
qwerescape
Is there a way to get the call stack or stack trace at any point in the code? Not from exceptions, but an expression that returns how the...
New
beno
I will often find my self writing things similar to: case some_value do nil -> something() "" -> something() _ -> someth...
New
lanycrost
Hi everyone! I need implement if…else if…else condition from my elixir code, and anymore of this control flow structures not work proper...
New

We're in Beta

About us Mission Statement