Turning The Arrows Around

Alice in Userland

Story

A new name appears in her issues. Chris.

Dear Alice,

I have multiple computers and syncing todos between them is a headache. I have a MySQL server. Could you add a MySQL backend so I can centralize my todos?

It is a fair thing to ask, and Alice already knows where it hurts. Every time she added a way to keep todos, the same question spread further through the file: is this the database or the folder? Adding a third would mean asking it a third way, in six places, and then a fourth way next time. The folders she made last week did nothing about this. MVC told her where the terminal and the browser belong. It said nothing about where the disk belongs.

The last pattern helped, so she goes looking again, and this time she finds Hexagonal Architecture. The idea underneath it is a strange one at first. Her code calls the database, so of course her code depends on the database. But it does not have to. She can write down what she needs from storage, as an interface, in her own words: give me the todos, find me one, save this, throw that away. That interface belongs to her. Then MySQL, SQLite and the folder each go and satisfy it. The arrow that used to point from her rules out to the database now points from the database back at her rules. That reversal has a name, the Dependency Inversion Principle, and once she sees it she cannot unsee it.

So she rearranges. TodoRepository goes into types.ts, four lines that say everything storage must be able to do. The old todo.ts becomes TodoService, a class in services, and it keeps only the rules: a todo needs a title, you cannot finish something that is still waiting, a todo with nothing unfinished behind it is ready. It no longer knows what a file is. Under repositories sit three small classes, one per storage, each one thinking about nothing but its own kind of storage. MysqlTodoRepository is the new one, and writing it changed no other file.

The last piece is the one that surprises her: nobody inside the program chooses a backend any more. main.ts reads the setting once, builds the repository it names, and hands it to TodoService, which hands itself to the controllers. Every class is given what it needs instead of reaching out and taking it. The if blocks are gone, all of them, and the place where the decision lives is now the one place a decision belongs, at the very edge, on the way in.

Alice's Notebook

  • Give every command its own function instead of writing its body inside the switch block.
  • Keep the code that reads the terminal apart from the code that knows what a todo is.
  • Handle different concerns in different parts of the program (Single Responsibility Principle).
  • Use a library for the work that is not your program’s real job.
  • Keep the rules in one place, so that you can put a new face on them without changing them.
  • Separate the user interaction mechanism completely from the core logic.
  • Depend on an interface that you own, not on the thing that does the work (Dependency Inversion Principle).
  • Give a class the things it needs from outside instead of letting it fetch them itself (Dependency Injection).

Choose a file from the tree.