DEV Community

Discussion on: Blazing with Phoenix: Project Structure

Collapse
 
lcezermf profile image
Luiz Cezer

Nice article, congratz!

I trying to use a similar approach but in this structure

- myapp
  - conversations
    - schemas
      - conversation.ex
    - use_cases
      - conversations
        - create.ex
        - list.ex
  - conversations.ex
- myapp_web

In this approach, I have conversations domain, then I have the schemas folder, a use case folder where the real actions live, and by the last, I have my context/bounded_context conversations.ex that will simple call some use case functions, for example:

defmodule YouSpeak.Conversations do
  alias YouSpeak.Conversations.UseCases.Conversations.{Create, List}

  def create_conversation(params), do: Create.call(params)

  def list_conversations(params), do: List.call(params)
end

This way everywhere I need to call and use case from the conversations domain, I just need to call this context/bounded_context, it will expose my public API to the outside world.

In the controller:

def create(conn, %{"conversation" => conversation_params}) do
    conversation_params =
      conversation_params
      |> Map.merge(%{"user_id" => conn.assigns.current_user.id})

    case YouSpeak.Conversations.create_conversation(conversation_params) do
      {:ok, _} ->
        conn
        |> put_flash(:info, "Conversation created")
        |> redirect(to: Routes.conversation_path(conn, :index))

      {:error, changeset} ->
        render(conn, "new.html", changeset: changeset)
    end
  end
Collapse
 
pedromtavares profile image
Pedro Tavares

Hey Luiz, thank you for sharing!

What is your reasoning to justify the use_cases folder? It seems like you could simplify things a bit by having create.ex and list.ex be defined as functions of a broader service module, cuts the indirection a little bit. I would also recommend naming child domains different from their parents, in this case you have 2 levels of abstraction called Conversations, can get a bit confusing.