revamp aggregate and use typestruct
All checks were successful
Build and Publish / build-release (push) Successful in 1m41s

This commit is contained in:
2026-04-22 11:37:04 -05:00
parent 5f2f9e9085
commit a7160aadcf
19 changed files with 228 additions and 185 deletions

View File

@@ -30,7 +30,7 @@ defmodule PolicyService.Aggregates.PolicyApplication do
ProviderQuoteReceived,
AllQuotesReceived,
QuoteAccepted,
SolicitationSent,
SolicitationRequestSent,
PolicyIssued
}
@@ -42,16 +42,14 @@ defmodule PolicyService.Aggregates.PolicyApplication do
:applicant_info,
:policy_details,
:selected_providers,
:accepted_quote_id,
:accepted_plan_id,
:accepted_provider_id,
:solicitation_id,
:policy_number,
:accepted_by,
:provider_policy_number,
:effective_date,
:expiry_date,
:state,
quotes: %{},
pending_endorsements: %{}
endorsements: %{}
]
# ── Execute ────────────────────────────────────────────────────────────
@@ -140,19 +138,29 @@ defmodule PolicyService.Aggregates.PolicyApplication do
end
def execute(%__MODULE__{} = agg, %AcceptQuoteAndSolicit{} = cmd) do
with {:ok, quote} <-
PolicyService.Aggregates.PolicyApplication.find_quote(agg, cmd.quote_id),
{:ok, provider} <-
PolicyService.Aggregates.PolicyApplication.find_provider(agg, quote.provider_id),
{:ok, plan} <-
PolicyService.Aggregates.PolicyApplication.find_plan(quote, cmd.plan_id) do
%QuoteAccepted{
id: agg.id,
quote: quote,
plan: plan,
provider: provider,
accepted_at: DateTime.utc_now()
}
case Enum.find_value(agg.quotes, fn {provider_id, quote} ->
case Enum.find(quote.plans, &(&1.plan_id == cmd.accepted_plan_id)) do
nil -> nil
plan -> {:ok, %{quote: quote, provider: provider_id, plan: plan}}
end
end) do
nil ->
{:error, :plan_not_found}
result ->
[
%QuoteAccepted{
id: agg.id,
quote: result.quote,
plan: result.plan,
provider: result.provider,
accepted_by: cmd.accepted_by
},
%SolicitationRequestSent{
id: agg.id,
plan: result.plan
}
]
end
end
@@ -162,7 +170,7 @@ defmodule PolicyService.Aggregates.PolicyApplication do
def execute(%__MODULE__{} = agg, %RecordPolicyIssued{} = cmd) do
%PolicyIssued{
id: agg.id,
policy_number: cmd.policy_number,
provider_policy_number: cmd.provider_policy_number,
effective_date: cmd.effective_date,
expiry_date: cmd.expiry_date,
issued_at: cmd.issued_at || DateTime.utc_now()
@@ -205,21 +213,22 @@ defmodule PolicyService.Aggregates.PolicyApplication do
def apply(%__MODULE__{} = agg, %QuoteAccepted{} = e) do
%__MODULE__{
agg
| accepted_quote_id: e.quote.quote_id,
accepted_plan_id: e.plan.plan_id,
accepted_provider_id: e.provider.provider_id,
state: :solicitation_sent
| accepted_plan_id: e.plan.plan_id,
accepted_by: e.accepted_by
}
end
def apply(%__MODULE__{} = agg, %SolicitationSent{} = e) do
%__MODULE__{agg | solicitation_id: e.solicitation_id}
def apply(%__MODULE__{} = agg, %SolicitationRequestSent{} = _e) do
%__MODULE__{
agg
| state: :awaiting_policy
}
end
def apply(%__MODULE__{} = agg, %PolicyIssued{} = e) do
%__MODULE__{
agg
| policy_number: e.policy_number,
| provider_policy_number: e.provider_policy_number,
effective_date: e.effective_date,
expiry_date: e.expiry_date,
state: :issued

View File

@@ -1,4 +1,9 @@
defmodule PolicyService.Aggregates.PolicyId do
@type t :: %__MODULE__{
org_id: String.t(),
policy_type: String.t(),
application_id: String.t()
}
@derive Jason.Encoder
defstruct [:org_id, :policy_type, :application_id]

View File

@@ -31,4 +31,4 @@ defmodule PolicyService.CommandedApp do
otp_app: :policy_service
router(PolicyService.Router)
end
end

View File

@@ -1,8 +1,6 @@
defmodule PolicyService.Commands.CarPolicy do
alias PolicyService.Commands.Policy
defmodule SubmitPolicyApplication, do: use(Policy.SubmitPolicyApplication)
defmodule RecordProviderQuote, do: use(Policy.RecordProviderQuote)
defmodule AcceptQuoteAndSolicit, do: use(Policy.AcceptQuoteAndSolicit)
defmodule RecordPolicyIssued, do: use(Policy.RecordPolicyIssued)
end
defmodule SubmitPolicyApplication, do: use(PolicyService.Commands.Policy.SubmitPolicyApplication)
defmodule RecordProviderQuote, do: use(PolicyService.Commands.Policy.RecordProviderQuote)
defmodule AcceptQuoteAndSolicit, do: use(PolicyService.Commands.Policy.AcceptQuoteAndSolicit)
defmodule RecordPolicyIssued, do: use(PolicyService.Commands.Policy.RecordPolicyIssued)
end

View File

@@ -1,8 +1,6 @@
defmodule PolicyService.Commands.FirePolicy do
alias PolicyService.Commands.Policy
defmodule SubmitPolicyApplication, do: use(Policy.SubmitPolicyApplication)
defmodule RecordProviderQuote, do: use(Policy.RecordProviderQuote)
defmodule AcceptQuoteAndSolicit, do: use(Policy.AcceptQuoteAndSolicit)
defmodule RecordPolicyIssued, do: use(Policy.RecordPolicyIssued)
end
defmodule SubmitPolicyApplication, do: use(PolicyService.Commands.Policy.SubmitPolicyApplication)
defmodule RecordProviderQuote, do: use(PolicyService.Commands.Policy.RecordProviderQuote)
defmodule AcceptQuoteAndSolicit, do: use(PolicyService.Commands.Policy.AcceptQuoteAndSolicit)
defmodule RecordPolicyIssued, do: use(PolicyService.Commands.Policy.RecordPolicyIssued)
end

View File

@@ -1,19 +1,20 @@
defmodule PolicyService.Commands.Policy do
@moduledoc """
Base templates for Policy commands.
Use these macros to ensure all policy types share the same structure.
Base templates for Policy commands using TypedStruct.
"""
defmodule SubmitPolicyApplication do
defmacro __using__(_opts) do
quote do
defstruct [
:id,
:submitted_by,
:applicant_info,
:policy_details,
:selected_providers
]
use TypedStruct
typedstruct do
field :id, PolicyService.Aggregates.PolicyId.t(), enforce: true
field :submitted_by, String.t(), enforce: true
field :applicant_info, map(), enforce: true
field :policy_details, map()
field :selected_providers, list(), enforce: true
end
end
end
end
@@ -21,16 +22,18 @@ defmodule PolicyService.Commands.Policy do
defmodule RecordProviderQuote do
defmacro __using__(_opts) do
quote do
defstruct [
:id,
:recorded_by,
:provider_id,
:quote_id,
:premium,
:coverage_details,
:valid_until,
:plans
]
use TypedStruct
typedstruct do
field :id, PolicyService.Aggregates.PolicyId.t(), enforce: true
field :recorded_by, String.t(), enforce: true
field :provider_id, String.t(), enforce: true
field :quote_id, String.t(), enforce: true
field :premium, String.t()
field :coverage_details, map()
field :valid_until, String.t(), enforce: true
field :plans, list(), enforce: true
end
end
end
end
@@ -38,13 +41,13 @@ defmodule PolicyService.Commands.Policy do
defmodule AcceptQuoteAndSolicit do
defmacro __using__(_opts) do
quote do
defstruct [
:id,
:accepted_by,
:quote_id,
:plan_id,
:solicitation_fields
]
use TypedStruct
typedstruct do
field :id, PolicyService.Aggregates.PolicyId.t(), enforce: true
field :accepted_by, String.t(), enforce: true
field :accepted_plan_id, String.t(), enforce: true
end
end
end
end
@@ -52,14 +55,16 @@ defmodule PolicyService.Commands.Policy do
defmodule RecordPolicyIssued do
defmacro __using__(_opts) do
quote do
defstruct [
:id,
:policy_number,
:effective_date,
:expiry_date,
:issued_at
]
use TypedStruct
typedstruct do
field :id, PolicyService.Aggregates.PolicyId.t(), enforce: true
field :provider_policy_number, String.t(), enforce: true
field :effective_date, String.t(), enforce: true
field :expiry_date, String.t(), enforce: true
field :issued_at, String.t()
end
end
end
end
end
end

View File

@@ -49,7 +49,7 @@ defmodule PolicyService.Consumers.PolicyIssuedConsumer do
"car" ->
%CarPolicy.RecordPolicyIssued{
id: event["id"],
policy_number: event["policy_number"],
provider_policy_number: event["provider_policy_number"],
effective_date: event["effective_date"],
expiry_date: event["expiry_date"],
issued_at: DateTime.utc_now()

View File

@@ -50,18 +50,18 @@ defmodule PolicyService.Events.Policy do
defmodule QuoteAccepted do
use PolicyService.Events
@derive Jason.Encoder
defstruct [:id, :accepted_by, :quote, :plan, :provider, :accepted_at]
defstruct [:id, :accepted_by, :quote, :plan, :provider]
end
defmodule SolicitationSent do
use PolicyService.Events
@derive Jason.Encoder
defstruct [:id, :solicitation_id, :provider_id, :template_id, :s3_key, :sent_at]
end
defmodule SolicitationRequestSent do
use PolicyService.Events
@derive Jason.Encoder
defstruct [:id, :plan]
end
defmodule PolicyIssued do
use PolicyService.Events
@derive Jason.Encoder
defstruct [:id, :policy_number, :effective_date, :expiry_date, :issued_at]
defstruct [:id, :provider_policy_number, :effective_date, :expiry_date, :issued_at]
end
end

View File

@@ -5,11 +5,11 @@ defmodule PolicyService.Handlers.SolicitationRequestHandler do
require Logger
alias PolicyService.Events.Policy.QuoteAccepted
alias PolicyService.MessageBus
alias PolicyService.Events.Policy.SolicitationRequestSent
alias PolicyService.MessageBus
def handle(%QuoteAccepted{} = event, _metadata) do
MessageBus.publish("policy_service.events.policy_issued", "quote.accepted", event)
:ok
end
def handle(%SolicitationRequestSent{} = event, _metadata) do
MessageBus.publish("policy_service.events.solicitation_requested", "solicitation.requested", event)
:ok
end
end

View File

@@ -11,7 +11,7 @@ defmodule PolicyService.Filters.PolicyApplicationFilters do
fragment("?->>'company_name' ilike ?", p.applicant_info, ^term) or
fragment("?->>'document_id' ilike ?", p.applicant_info, ^term) or
fragment("?->>'ruc' ilike ?", p.applicant_info, ^term) or
ilike(p.policy_number, ^term)
ilike(p.provider_policy_number, ^term)
)
end
end

View File

@@ -2,34 +2,29 @@ defmodule PolicyService.Projections.PolicyApplication do
use Ecto.Schema
@derive {Jason.Encoder,
only: [
:id,
:application_id,
:org_id,
:submitted_by,
:policy_type,
:applicant_info,
:policy_details,
:selected_providers,
:quotes,
:accepted_quote_id,
:accepted_plan_id,
:accepted_provider_id,
:accepted_by,
:accepted_at,
:solicitation_id,
:solicitation_s3_key,
:policy_number,
:premium,
:effective_date,
:expiry_date,
:status,
:submitted_at,
:solicitation_sent_at,
:issued_at,
:inserted_at,
:updated_at
]}
only: [
:id,
:application_id,
:org_id,
:submitted_by,
:policy_type,
:applicant_info,
:policy_details,
:selected_providers,
:quotes,
:accepted_plan_id,
:accepted_by,
:provider_policy_number,
:premium,
:effective_date,
:expiry_date,
:status,
:submitted_at,
:solicitation_sent_at,
:issued_at,
:inserted_at,
:updated_at
]}
@derive {
Flop.Schema,
@@ -61,16 +56,11 @@ defmodule PolicyService.Projections.PolicyApplication do
field :selected_providers, {:array, :string}, default: []
field :quotes, :map, default: %{}
field :accepted_quote_id, :string
field :accepted_plan_id, :string
field :accepted_provider_id, :string
field :accepted_by, :string
field :accepted_at, :utc_datetime_usec
field :solicitation_id, :string
field :solicitation_s3_key, :string
field :provider_policy_number, :string
field :policy_number, :string
field :premium, :decimal
field :effective_date, :date
field :expiry_date, :date

View File

@@ -10,12 +10,11 @@ defmodule PolicyService.Projectors.PolicyProjector do
ProviderQuoteReceived,
AllQuotesReceived,
QuoteAccepted,
SolicitationSent,
SolicitationRequestSent,
PolicyIssued
}
alias PolicyService.Projections.PolicyApplication
alias PolicyService.Aggregates.PolicyId
import Ecto.Query
project(%PolicyApplicationSubmitted{} = e, _meta, fn multi ->
@@ -69,25 +68,20 @@ defmodule PolicyService.Projectors.PolicyProjector do
end)
|> Ecto.Multi.update(:policy_application, fn %{fetch: p} ->
Ecto.Changeset.change(p,
accepted_quote_id: e.quote.quote_id,
accepted_plan_id: e.plan.plan_id,
accepted_provider_id: e.provider.id,
accepted_at: parse_datetime(e.accepted_at),
status: "solicitation_sent"
accepted_by: e.accepted_by
)
end)
end)
project(%SolicitationSent{} = e, _meta, fn multi ->
project(%SolicitationRequestSent{} = e, _meta, fn multi ->
multi
|> Ecto.Multi.run(:fetch, fn repo, _ ->
{:ok, repo.get!(PolicyApplication, to_string(e.id))}
end)
|> Ecto.Multi.update(:policy_application, fn %{fetch: p} ->
Ecto.Changeset.change(p,
solicitation_id: e.solicitation_id,
solicitation_s3_key: e.s3_key,
solicitation_sent_at: parse_datetime(e.sent_at)
status: "awaiting_policy"
)
end)
end)
@@ -99,7 +93,7 @@ defmodule PolicyService.Projectors.PolicyProjector do
end)
|> Ecto.Multi.update(:policy_application, fn %{fetch: p} ->
Ecto.Changeset.change(p,
policy_number: e.policy_number,
provider_policy_number: e.provider_policy_number,
effective_date: parse_date(e.effective_date),
expiry_date: parse_date(e.expiry_date),
issued_at: parse_datetime(e.issued_at),