# Where do you put code that builds a resource from the same context?

The default generated code in phoenix creates functions like this:

def create_post(attrs \\ %{}) do
%Post{}
|> Post.changeset(attrs)
|> Repo.insert()
end

However typically code has to be filters/cleaned, associations checked, etc… before one can actually create_post (as in this example).

I see two ways of doing the work before creating a post, either in the function, or as part of another function.

# option 1: in function

def create_post(attrs \\ %{}) do
# check author is a publisher on the platform maybe?
# check if this publication is accepting new posts?
# maybe can only publish post if author has filled out their profile?
# collect metadata not coming in attrs maybe?
%Post{}
|> Post.changeset(attrs)
|> Repo.insert()
end

# option 2: other function

# *_service?
def post_service(attrs \\ {}) do
# check author is a publisher on the platform maybe?
# check if this publication is accepting new posts?
# maybe can only publish post if author has filled out their profile?
# collect metadata not coming in attrs maybe?

create_post(attrs)
end

Assuming all those comments I added belong to functions in the same context, which style do you prefer and why? Please note, the end goal of the function is to Create A Post (not asking about side-effects/associations/callbacks/etc…).

Thank you for all opinions/suggestions.

Of the two options you presented, I would personally add your validations to the create_post function. Otherwise, in the future someone may use the create_post function without knowing that post_service exists, thereby skipping validations. Validations are a part of your business logic, and so should be included in any CRUD operations you perform. Leaving the option to skip validations is fine, making it just as easy as doing validations is not. There’s Ecto.Changeset.change if you want to put your changes directly in the DB with no checking, but anyone doing this should know what they’re doing and why.

2 Likes