I have a Class struct which contains a set of Topics. I am
trying to treat Class as an Aggregate root in DDD parlance. Which
means all updates to sub-entities of Class (Topics in this case)
have to be managed by the Class. This is to maintain invariants within
the Class and provide transactional consistency within Class and its
sub entities.
Here is an outline of what I have:
def changeset(class, attrs) do
class
|> cast(attrs, [:name,...])
|> cast_assoc(:class_topics)
|> validate_required([:name, ...])
end
def create_topic(%Class{} = class, attrs \\ %{}) do
number = length(class.topics) + 1
new_topic =
class.topics
|> Enum.concat([Map.put(attrs, "number", number)])
result = class
|> Class.changeset(%{topics: new_topic})
|> Repo.update()
end
This complains about cast_assoc requiring Maps (whereas in this case
Topic structs are supplied)
So I updated it to this:
def create_topic(%Class{} = class, attrs \\ %{}) do
number = length(class.topics) + 1
new_topic =
class.topics
|> Enum.map(fn i -> %{id: i.id} end)
|> Enum.concat([Map.put(attrs, "number", number)])
result = class
|> Class.changeset(%{topics: new_topic})
|> Repo.update()
end
Which also doesn’t achieve what I want.
I am doing it like this as there are additional constraints on the topics
being created - which prevents me from just creating a topic without first
preloading and validating against existing topics. (For example, I want
to ensure that there is at most 1 Topic which has a particular category
in a Class - which requires me to have them all available to validate this
when creating or updating a topic.)
I think there is something obvious I am not understanding - so I thought
I’d post the question here.
Any help would be appreciated.






















