A few weeks ago — in the midst of PyCon Canada planning no less — my girlfriend complained about the Toronto Public Library’s policy of only emailing you after your books are overdue. Having experienced this anguish firsthand before, I threw together a something to fix it.
I really like attacking random problems like these because I never know what I’m going to learn by doing it. In this project, I specifically took myself out of my comfort zone by abandoning my usual way of doing things. Normally, I’ll take a domain-driven design approach to things, where I try my best to seperate infrastructure from core domain logic.
In cases like this one where there isn’t much of a “domain” to speak of, though, this has the potential to be overkill. That’s why I decided to experiment with going entirely in the opposite direction. I used the framework (specifically the ORM) as if it were some ubiquitous thing, ready to be used anywhere. (Which it is, of course; and therein lies its power and its greatest threat.) If I was in a view function and I needed to pull some data, I just did it right there instead of pushing that functionality behind a service layer.
I actually made it all the way to the end by doing things this way before I started to feel really uncomfortable and confused with the whole thing, even for a project this small. One of the benefits of practicing a technique over and over again is that you establish an Everything In Its Right Place habit where you never really have to think about where something should go. When you abandon that recipe or method, it sort of simulates the feeling you get when multiple people are working on the system without sharing a consistent metaphor of how it works. That is, you’re constantly confused. So, right near the end, I caved and pushed a bunch of stuff behind a service layer, using a hacky barebones dependency injection trick I like to play in Django.
The one concession I made to the framework was to keep some infrastructure-specific stuff in behaviors on the domain objects themselves. I did this because I find in invasive frameworks like Django, there is a tendency to want to give an object a responsibility (e.g. “tell me what your absolute url is”) that should really be behind a service layer by virtue of that behaviour relying on the infrastructure (in the case, calling Django’s url reverse function, for example). I wanted to see how it felt to give into this tendency, given that you’re already binding your model objects to the framework in Django through inheritance anyways. It certainly made things a bit more convenient. I learned that there are middle grounds of abstraction you can take that will make the organization of a framework-reliant project a bit easier, without going for the whole hog and allowing you to swap things out at will.
For the non-programmers in the audience, I hope you find this thing useful. But even if you don’t, I still had fun building it.