What's your process on splitting application logic?

I personally don’t think that changeset functions belong to schema. About a year ago I’ve started consulting an agency, and proposed the team to write changesets as private functions in context modules, and that’s been working really well. In most cases I’ve seen, a changeset function is used in exactly one place, which is not a schema, so making this function public and moving it to another module makes little sense to me, and it complicates the reading experience. Even when it’s used in multiple places, there’s typically a strong cohesion between all callers (they work on the same data), so they are a part of the same context module, and hence changeset as a private function in that same module works fine.

We usually don’t have functions in schemas, but we may add some helpers for computing derived properties. E.g. say that the OrderItem schema has fields quantity and price. If I need a function total_price, I’d define it in the schema.

We mostly stay away from virtual fields. I think there was one case where we used it to store some derived value computed at the database level.

In context modules.

They are our main domain types, since we don’t transfer data from schemas to pure Ecto-independent data structures.

Schemas are returned from contexts to web tier, and they are used by controllers, resolvers, and views to produce the output. Therefore, they are not an internal implementation detail of contexts.

Note that I’m not suggesting this as a universal pattern, but it’s been working well for the client’s projects, which I’d categorize as small-to-moderately complex.

3 Likes