Going with the Workflow

Learn about design thinking, business logic workflow, workflow classes, and how to write tests going with the workflow.

Design thinking

Now we need to make some decisions. We have some logic that goes beyond Rails boilerplate—namely, we need to parse that list of tasks and create Task instances out of them when the form is submitted. That code needs to go somewhere, and the unit tests we’re about to write against that code need to know where that place is. This is where the design thinking comes in the TDD process.

No matter where we put the actual coding logic, Rails will still insist on the existence of a controller, so we have the separate decision of how (or whether) to test whatever logic winds up in the controller itself.

Let’s start with the business logic; we’ll come back to the controller.

Business logic workflow

Three locations are commonly used for business logic that responds to user input beyond the common "pass the params hash to ActiveRecord#create" Rails behavior. Here are the options:

  • Put the extra logic in the controller. This is often the Rails core team’s preferred method, and if there isn’t much logic, it works perfectly fine. In my experience, this location doesn’t work as well for complex logic. It’s challenging to test, awkward to refactor, and difficult to share if that becomes an issue. It also becomes confusing if there is more than one complicated action in the controller.

  • Put the extra logic in the associated model, often at least partially in a class method. This was my go-to move for years. It’s somewhat easier to test, but still kind of awkward to refactor—Ruby class method semantics are a pain. It also makes the model more complicated and doesn’t really help manage actions that legitimately span multiple models, as this one does since it’s creating a Project and multiple Tasks.

  • Create a separate class to encapsulate the logic and workflow. This tends to be my first choice these days. It’s the easiest to test and the best able to manage complexity changes as they come. The main downside is we wind up with many little classes, but we don’t mind having many little pieces anyway.

Placing business logic outside Rails classes makes that logic easier to test and manage.

So, we’re creating a new class. We’ll stress here that this design is not the only way to go, and if we feel the complexity of this particular action doesn’t warrant its own class, that’s fine. There is no consistent generic name for a logic class like this. Let’s call it a workflow class. Other names we might see in use for different kinds of logic classes include:

  • action,
  • service,
  • context,
  • use case,
  • concern, and
  • factory.

Workflow classes

Now we’d like to show how we can use TDD to move logic outside the Rails classes. The workflow class needs to create a project from a name and a list of tasks. Let’s start with the name:

Get hands-on with 1200+ tech skills courses.