Python task manager from scratch, part 39: Thinking about interfaces, again

Last things first this time. Between part 38 and this part the relevant changes are:

  1. Moving the Event class and its subclass into a separate file;
  2. Doing the same with Command;
  3. Defining an interface for the repository corresponding to (what I called) a KickoffCoordinator.

There is no new functionality, but enough can go wrong between the last installment and this one that I want to provide a separate set of notes.

  1. It's tempting to think about all the Events and Commands you might ever need and define them now, both because it feels responsible and plan-ahead-y and because it can feel silly to define an empty class meant to be subclassed and then only provide one subclass. If there are some you know you'll need, go ahead. But it's easy to go wrong (and spend time inefficiently) if you guess in advance at what you'll need. Having Event and Command in place is more than enough planning. With this structure in place, we're not going to have a hard time adding any necessary items later. Further planning now therefore gains us no safety.
  2. It might seem counterintuitive to distinguish between events and commands (in such a deep way) at all. The best discussion I know of this is in Architecture Patterns with Python, but Google tells me that there is also no lack of people opining on the Internet about how important it is.
  3. We will need to persist KickoffCoordinators somehow. (I'm not sold on the name: whatever it is in virtue of which my watering the plants means that I need to do it again after a while, that's what we're talking about.) It's tempting to create an extra field in the task SQL table and put that information there. I think this would be a serious mistake. The whole purpose of distinguishing between the coordinator and the task is that they're different things. That probably means they need different database tables to be persisted accurately. And adding several extra columns to the task table is a heavy cost in complexity.
  4. At the same time, it's tempting to go wild and try to imagine in advance all the different coordination mechanisms we might need, defining an elaborate coordinator table correspondingly. I think this is a mistake, too. If other coordinators are way more complex, we can define other SQL database tables (or even persistence mechanisms that are not SQL database tables) for them. That can all be handled at the repository level. It's no problem to have a table structured just for (what I've called) KickoffCoordinators.
  5. And, as always, thinking about what interface the repository should present should come first. Perhaps persistence details will force you into modifying that interface (though this is necessary less than people tend to think), but at least start with the way you think things ought to be, independent of your actual futzing with persistence details.

Finally, the advanced mechanics of message-passing systems is a deep topic (easily enough to spend a career studying and specializing in). This, by contrast, is intended as enough infrastructure to allow us to build a clean system at modest scale (or perhaps just for personal use). I won't (at least for the next few hundred or so installments) be worried about:

  1. Being robust against most or all concurrency issues;
  2. Building enough abstraction to support many dozens of kinds of interrelated events with complicated interdependencies;
  3. Handling millions of task updates per second.

Here's the current commit in the veery/ repository.

Next post: Python task manager from scratch, part 40: Persisting task coordinators

Home page