No More Services, No More Repositories
Tags: programming, codequality, discuss
Published: 2022-01-14 20:14:35

I recently read [Modern Software Engineering: Doing What Works to Build Better Software Faster](https://www.amazon.ca/-/fr/David-Farley/dp/0137314914) by David Farley. This article explains, technically, one way, described in the book, that will make your code better.

## What is great code?

*All the example provided will revolve around a cart in which you can add or remove some items.*

Great code is easy to understand, testable, has minimum coupling, etc. One of the qualities that we will often hear is that it does one thing only.

### The very bad

In some codebases, we will see code like this one:

```python
def add_to_cart(self, item):
    self.cart.add(item)
 
    conn = sqlite3.connect('my_db.sqlite')
    cur = conn.cursor()
    cur.execute('INSERT INTO cart (name, price) values (item.name, item.price)')
    conn.commit()
    conn.close()
 
    return self.calculate_cart_total()
```

*This code is (not exactly) from "Modern Software Engineering: Doing What Works to Build Better Software Faster" by David Farley, page 240*

In the above example, our code is highly coupled. This is often considered to be bad code since everything is bound together, it is "hard" to read, and is hard to test.

### A Bit Better

In a lot of teams I joined through my career, most organize their code around services. As an example, the code will contain a `CartService` class which might contain multiple methods. An example implementation might look like this:

```python
class CartService
    def __init__(self, repository):
        self.repository = repository

    def add(item: Item):
        ...

    def get_items():
        ...

    ...
```

This is arguably a better alternative to our above function as the access to our database has been abstracted away behind a repository. This way:

- our add method will be easier to read,
- our service is testable,
- our implementation is not coupled to sqlite: we could easily inject a different repository implementation.

Even if the above code is better, I tend to push the teams I join away from it:

The service approach often grows in a bad way. You will always have more than 2 or 3 methods per service and these methods all have different dependencies. In the end, you are still getting highly coupled code, since all your `Cart` related code is shipped together.

### One Step Further

I tend to organize my code around use cases. Use cases are a great way of organizing code as it isolates your processes independently from one another.

“One class, one thing. One method, one thing.”

This motto is great when you wish to reduce cohesion. If we use the same cart example as above, our `add_to_cart` use case would look like this:

```python
class AddToCart:
    def add_to_cart(self, item):
        self.cart.add(item)
        self.repository.add(item)
        return self.calculate_cart_total()
```

In this case, our "service" contains only one function: "add_to_cart", and is responsible for one process: the one of adding items to our cart. This "use case" can be injected anywhere and will not pull a lot of useless dependencies as the "service" approach will.

This said, there is still one caveat: the repository.

The repository has the same problem our service had... It contains all the logic responsible for interaction with the persistence part of our code...

### Best Approach

In his book, David Farley does not even mention repositories. Instead, he shares 2 alternative implementations:

```python
def add_to_cart2(self, item):
    self.cart.add(item)
    self.store.store_item(item)
    return self.calculate_cart_total();
 
def add_to_cart3(self, item, listener):
    self.cart.add(item)
    listener.on_item_added(self, item)
```

*Code from "Modern Software Engineering: Doing What Works to Build Better Software Faster" by David Farley, page 240*

His book discusses the advantages of using one version versus the other, but this is not what I want to discuss here (I encourage you to read the book to find out).

What is great here is that the "repository" concept:

- is abstracted: either completely hidden from the implementation (add_to_cart3) or replaced by a store (add_to_cart2).
- requires an implementation that does one thing only.

In `add_to_cart2`, our "repository" is reduced to another use case the one of storing the item via the store.

One of the examples shown in the book is the following:

```java
public interface WordSource
{
    String[] words();
}
```

This is a perfect "repository" abstraction that does only one thing.

## Summary

“One class, one thing. One method, one thing.”