This commit is contained in:
Haim Kortovich
2026-03-05 11:35:01 -05:00
commit 072dbf6e66
43 changed files with 1400 additions and 0 deletions

View File

@@ -0,0 +1,44 @@
defmodule CustomerService.Aggregates.Customer do
defstruct [
:id,
:first_name,
:last_name,
:birth_date,
:gender,
:email,
:phone
]
alias __MODULE__
alias Commanded.Aggregates.Aggregate
alias CustomerService.Commands
alias CustomerService.Events
@behaviour Aggregate
@impl Aggregate
def execute(%Customer{id: nil}, %Commands.CreateCustomer{} = cmd) do
%Events.CustomerCreated{
id: cmd.id,
first_name: cmd.first_name,
last_name: cmd.last_name,
birth_date: cmd.birth_date,
gender: cmd.gender,
email: cmd.email,
phone: cmd.phone
}
end
@impl Aggregate
def apply(%Customer{} = c, %Events.CustomerCreated{} = e) do
%Customer{
c
| id: e.id,
first_name: e.first_name,
last_name: e.last_name,
birth_date: e.birth_date,
gender: e.gender,
email: e.email,
phone: e.phone
}
end
end

View File

@@ -0,0 +1,36 @@
defmodule CustomerService.Application do
# See https://hexdocs.pm/elixir/Application.html
# for more information on OTP Applications
@moduledoc false
use Application
@impl true
def start(_type, _args) do
children = [
CustomerService.CommandedApp,
CustomerService.Repo,
CustomerService.Projectors.Customer,
CustomerServiceWeb.Telemetry,
{DNSCluster, query: Application.get_env(:customer_service, :dns_cluster_query) || :ignore},
{Phoenix.PubSub, name: CustomerService.PubSub},
# Start a worker by calling: CustomerService.Worker.start_link(arg)
# {CustomerService.Worker, arg},
# Start to serve requests, typically the last entry
CustomerServiceWeb.Endpoint
]
# See https://hexdocs.pm/elixir/Supervisor.html
# for other strategies and supported options
opts = [strategy: :one_for_one, name: CustomerService.Supervisor]
Supervisor.start_link(children, opts)
end
# Tell Phoenix to update the endpoint configuration
# whenever the application is updated.
@impl true
def config_change(changed, _new, removed) do
CustomerServiceWeb.Endpoint.config_change(changed, removed)
:ok
end
end

View File

@@ -0,0 +1,15 @@
defmodule CustomerService.Router do
use Commanded.Commands.Router
alias CustomerService.Commands
alias CustomerService.Aggregates
identify(Aggregates.Customer, by: :id)
dispatch([Commands.CreateCustomer], to: Aggregates.Customer)
end
defmodule CustomerService.CommandedApp do
use Commanded.Application,
otp_app: :customer_service
router(CustomerService.Router)
end

View File

@@ -0,0 +1,11 @@
defmodule CustomerService.Commands.CreateCustomer do
defstruct [
:id,
:first_name,
:last_name,
:birth_date,
:gender,
:email,
:phone
]
end

View File

@@ -0,0 +1,3 @@
defmodule CustomerService.EventStore do
use EventStore, otp_app: :customer_service
end

View File

@@ -0,0 +1,12 @@
defmodule CustomerService.Events.CustomerCreated do
@derive Jason.Encoder
defstruct [
:id,
:first_name,
:last_name,
:birth_date,
:gender,
:email,
:phone
]
end

View File

@@ -0,0 +1,28 @@
defmodule CustomerService.Projections.Customer do
use Ecto.Schema
@derive {Jason.Encoder,
only: [
:id,
:first_name,
:last_name,
:birth_date,
:gender,
:email,
:phone,
:inserted_at,
:updated_at
]}
@primary_key {:id, :binary_id, autogenerate: false}
@timestamps_opts [type: :utc_datetime_usec]
schema "customers" do
field :first_name, :string
field :last_name, :string
field :birth_date, :date
field :gender, :string
field :email, :string
field :phone, :string
timestamps()
end
end

View File

@@ -0,0 +1,31 @@
defmodule CustomerService.Projectors.Customer do
use Commanded.Projections.Ecto,
application: CustomerService.CommandedApp,
repo: CustomerService.Repo,
name: "CustomerService.Projetors.Customer",
consistency: :strong
alias CustomerService.Events
alias CustomerService.Projections.Customer
project(%Events.CustomerCreated{} = event, fn multi ->
Ecto.Multi.insert(multi, :customer, %Customer{
id: event.id,
first_name: event.first_name,
last_name: event.last_name,
birth_date: event.birth_date,
gender: event.gender,
email: event.email,
phone: event.phone
})
end)
# project %Events.CustomerDeactivated{} = event, _metadata do
# Ecto.Multi.update_all(
# multi,
# :deactivate_customer,
# from(c in Customer, where: c.customer_id == ^event.customer_id),
# set: [active: false]
# )
# end
end

View File

@@ -0,0 +1,13 @@
defmodule CustomerService.Repo do
use Ecto.Repo,
otp_app: :customer_service,
adapter: Ecto.Adapters.Postgres
@doc """
Dynamically loads the repository url from the
DATABASE_URL environment variable.
"""
def init(_, opts) do
{:ok, Keyword.put(opts, :url, System.get_env("DATABASE_URL"))}
end
end