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