Structuring a new Phoenix Project Photo by on William Bout Unsplash This blog post is part of a series of posts detailing the process of . The previous entries have been: development AlloyCI Part 1: Starting a new Project If you are coming from a Rails background, you might find the folder structure of a Phoenix project a bit weird, specially after the changes in 1.3. But don’t be put off by this, the structure actually makes a lot of sense, specially the new 1.3 structure. Unlike Rails, there is no folder where most of your code will live. Phoenix 1.3 projects follow the structure of pretty much any regular Elixir project, this means that most of your code will live inside the folder. app/ lib/my_app As you might now, Phoenix 1.3 changed the way projects are structured. Before this change most of your code would be under , with little to no further organizations. You would have a directory, where you would put all your business logic, without much consideration as to how they interact together. This folder would get pretty large, pretty fast. Also the name seems to imply an object, but in Elixir, there are no objects, so storing your data access layer under a folder named makes little contextual sense. web/ models model models Data & Contexts Phoenix 1.3, by default, guides you towards a better way of organizing your code. Controllers, Views, Templates go under , database migrations and related files go under , and the rest of your Elixir code will live under . This is the basic setup, and you can tweak it, and change it as much as you like. You have complete liberty as to how to organize your code. lib/my_app_web priv/repo lib/my_app Since I started writing AlloyCI Phoenix 1.3 was fully released, some of the folder structure is different than the one the latest 1.3 generators create. I prefer the way AlloyCI is structured right now, because I really don’t like the way they renamed the folder to and everything inside it from to . I really prefer the separation in the module name, and the fact that the app name is not repeated in the web folder name. Thanks to the flexibility Phoenix provides, I don’t need to follow the conventions, though. before web/ alloy_ci_web/ AlloyCi.Web.XXX AlloyCiWeb.XXX Anyways, the most important part about the structure changes, is that Phoenix now guides you towards using for structuring your data access layer. contexts Using AlloyCI as an example, we have the context (which is under folder), where the , , and schemas live. These 3 schemas are closely related, and belong to the same business logic, namely the handling of Accounts. Accounts lib/alloy_ci/accounts User Authentication Installation If you look closely at the under the folder, you will see that there are no functions in the schema files, other than the changeset function. This means that I would need to either go straight through to manipulate the database data (not recommended) or that I need an API boundary that will let me perform actions on the accounts related schemas. files accounts Ecto This is where the module comes into play. This module is the boundary with which AlloyCI will interact if it needs to perform an action on any of the schemas related to an Account. All public functions of this module provide an easy way to manipulate the data, while providing security through a layer of indirection. AlloyCi.Accounts This is the purpose of They provide a boundary between your data layer and your business logic, and allow you to have an explicit contract that tells you how you can manipulate your data. It also allows you to stay independent from . contexts. Ecto Let’s say, in the future, you’d like to switch from to the “latest, coolestDB driver”. If you didn’t use an abstraction layer, like the contexts, you would have to refactor every function across the codebase that used to communicate to the data layer. since we are using contexts, we would only need to refactor the code inside the context itself. Ecto Ecto But Data Presentation The code that will actually present your data to the user can live under the or folders, depending on how you want to structure it (the automatic generator will default to but I prefer the former). lib/my_app/web lib/my_app_web lib/my_app_web In here you will find the folders where your controllers, views, templates and channels will live. Let’s start with the presentation layer. Views & Templates If you come from a Rails background, you might wonder why there are two components to presenting the data, when in Rails all you need is the folder. In Phoenix, the “views” are not composed of templated HTML files, but rather they are regular Elixir modules. These modules are there to help you share code with the template, and fulfill a similar purpose as the Rails “View Helpers”, but are, by default, specific to a single controller (other views are not loaded, unlike Rails that loads all view helpers, regardless of the controller being called). This separation makes it easier to use the same signature on similar helper functions needed to present data (without really overloading them), depending on which controller is being called, thus simplifying your code. views The templates are, then, where your HTML code lives. The template files are saved as files (meaning embedded Elixir), and are very similar to files. The syntax is exactly the same, but instead of Ruby code inside, you write Elixir code 😄 *.html.eex erb A very important distinction between Phoenix and Rails is how you share information between the controller and the template. In Rails, it is enough to declare an instance variable with and it will be available to the template/view. @something Given the functional nature of Elixir, in Phoenix you need to explicitly pass the information you wish to be available to the views in the function. These are called assigns. As an example, here is the action of the : render show PipelineController Everything that comes after are the assigns, so the variables available to the templates related to the action are , , and . We can see an example of how to use them in this snippet from the pipeline info header: "show.html" show builds pipeline current_user Once a variable has been assigned, it is available to the template via , just like with Rails. Functions defined the view file of the same name as the controller (in this example ) are immediately available to the template. In the above example, creates an HTML link to the specific commit on GitHub. @var_name pipeline_view.ex sha_link/1 Controllers In structure, Phoenix Controllers are very similar to Rails Controllers, with the main difference being described above. When generated by the helper tools, they will the same index, show, edit, update, and delete actions as their Rails counterparts. And just as with Rails Controllers, you can define any action you desire by defining a function, and connecting a route to it. Channels Phoenix Channels are used to communicate with the web client via Web Sockets. They are similar to in Rails, but in my opinion, much more powerful, and . In AlloyCI, they are used to push the output of the build logs in real time, and to receive a pre formatted piece of HTML code to show the user’s repositories (more on how AlloyCI uses Channels will be discussed in another post). ActionCable performant And there you have it. That is the basic structure of a Phoenix project. There are other components that we haven’t covered here, like , or . We will discuss these advanced topics in a future blog post. Plugs Background Jobs