Structuring projects - this ok?

Nefcairon
Sat, 31 Aug 2019 13:05:06 +0000
Questions / Help

Hello,

With your help from Generate a list of structs from a list and Improve a function I have now a working first simple version of my program. Before I want to extend that program, I am wondering whether the structure of my project is a good one or not.

My program just gets the data of the first 100.000 leagues (a “league” has an id, name, etc.) from an external url and inserts them into a repo.

It looks like this (I stripped the files as much as possible):

program.ex

defmodule Program do
  use Application

  alias Program.LeagueAPI

  def start(_type, _args) do
    children = [
      Program.Repo
    ]

    Supervisor.start_link(children, strategy: :one_for_one)
  end

  def start() do
    for x <- 1..100_000 do
      LeagueAPI.get_data(x)
    end
  end
end

leagueAPI.ex is like that:

defmodule Program.LeagueAPI do

  @api_url "http://--hidden-external-api-url"

  alias ProgramAuth
  alias Program.Repo
  alias League
  import SweetXml

  defp get_url(league_id) do
    @api_url <> "&leagueID=" <> to_string(league_id)
  end

  def get_data(league_id) do
    url = get_url(league_id)
    response = ProgramAuth.get_response(url)

    league_id =
      response.body
      |> xpath(~x"/ProgramData/LeagueID/text()"l)
      |> List.first()
      |> to_string()

    league_name =
      response.body
      |> xpath(~x"/ProgramData/LeagueName/text()"l)
      |> List.first()
      |> to_string()

    # same for all the other fields

    %Program.League{}
    |> Ecto.Changeset.change(%{
      league_id: league_id,
      # ... same for all the other fields
    })
    |> Repo.insert(on_conflict: :nothing)
  end
end

and finally league.ex is like that:

defmodule Program.League do
  use Ecto.Schema
  import Ecto.Changeset

  schema "leagues" do
    field(:league_id, :string, unique: true)
    # same for all the other fields
    timestamps()
  end

  def changeset(league, attrs) do
    league
    |> cast(attrs, [
      :league_id,
      # same for all the other fields
    ])
    |> validate_required([:league_id])
    |> unique_constraint(:league_id)
    |> validate_number(:league_id, greater_than_or_equal_to: Decimal.new(0))
    # same for all the other fields
  end
end

Is my structure with a file for the main program, one file for the schema and one for the API with the functions I implemented in there a good practise or should I put them differently?

Especially I am not sure where the Repo.insert should be placed. Should a module called leagueAPI know the Repo?
Now Repo.insert is in the leagueAPI.ex, but maybe it should be in program.ex and leaguesAPI should only return a league (which is a schema) to program which inserts it? But should leaguesAPI know the schema “league”? Maybe it should return a list/struct instead?

It may seem that I overcomplicate things, but I think it’s better to think in correct structure before attempting much heavier implementations.

Thanks for advice

6 posts - 2 participants