1. Never access models directly from controllers. Build an API layer that exposes discrete methods which can store and retrieve data, and use these exclusively from the higher layers of your app. Using the example given in the article, (using a hypothetical Python + SQLAlchemy app):
This:
post = Post.query.get(post_id)
posts_by_date = Post.query.filter(Post.date >= start_date).filter(Post.date <= end_date).all()
posts_by_author = Post.query.filter(Post.author_id = user_id)
Becomes: post = api.get_post(id=post_id)
posts_by_date = api.get_posts(before=end_date, after=start_date)
posts_by_author = api.get_posts(author_id=user_id)
2. APIs should not use auto-querying collections whenever possible, and should accept configurable options to use an abstract storage class representing tables, buckets, interfaces to externals services or whatever.So, this:
def get_post(id):
return Post.query.options(joinedload(Post.comments)).filter(Post.id == id).one()
becomes: def get_post(id):
return PostStore.query(post_id=id, with_comments=True)
Essentially, the idea is to channel access to the data store through only discrete paths. First, split the model layer into methods that support higher layer needs -- these are what Martin is calling the dependencies I think. Identify methods like `get_post` which contains logic and information such as how to get posts by id, and whether or not comments should be eager loaded.Second, abstract the actual dispatch of queries to the data store into domain-specific stores. Instead of using a generic data model, write stores that know about Posts, Comments, Users etc and wrap the generic model classes. In this way, a RelationalPostStore knows what it means to get a post from the store, along with its comments, users, author information etc. A quickly changed user-configurable setting can switch that out with an HDFSPostStore, which knows how to get those objects from a Hadoop backend. A CouchPostStore can do the same, etc.
This is the pattern that has been emerging through my own repeated web dev experiences. I'd be interested to know if there are obvious/subtle improvements or flaws.