Sean Upton - A Pinch of Indirection
Talk by Sean Upton at the Plone Conference 2016 in Boston.
I am from the University of Utah Department of Pediatrics, http://upiq.org. I will be giving practical tips for using component architectures. This is my pragmatic perspective on components, my opinion. We will explore idioms and ideas.
Plone has been using the ZCA (Zope Component Architecture) for most of the past fifteen years. It is an old hat, but it fits great, we don't want another.
In Plone we push to make things simpler. An API helps here. But you should still strive to understand the underlying component architecture, otherwise lots of things won't make sense. We have complexity, which may be hard to debug, but it makes great things possible.
I encountered the ZCA for the first time in a discussion on OSCON in 2002 with Jim Fulton. Zope 2 at that time had lots of difficult code, look at all the mixin classes in for example the OFS package. We still have that today. But the ZCA allows a different approach. Instead of one big package, there are more packages, which is a trade-off.
Holy trinity of components:
- adapters: single context. Or views with multiple contexts (object and request). Or subscribers.
- resources: content, utilities, request/response
Objects can be in two categories, those that you lookup and those that you don't. Lookup by path, component registry or both. Others are created on the fly, like site global state.
Indirection. If you want to override a widget with a custom widget and the override is not working, then it can be tricky to see why this is. It can be a scary departure from imperative programming, where things may be clearer at first, but the override may not even be possible in such a system.
Interfaces are contracts. Multiple implementations can provide this interface.
Make components that look like native data structures. Programmers are used to it, so let them make use of their experience, don't work against it. We use a resource-centric development, driven by state, not by action. We do object publishing, so keep the object central. A procedural API, like plone.api, will often try to hide the ZCA: just ask the site for something, which then makes the single site central. You don't notice that under the hood plone.api is using the ZCA. But this abstraction leaks through.
I can and do use plone.api. But I also wrote my own, more state-driven api. I can mix these.
My point: don't think that an API needs to hide the ZCA, that the ZCA is only for ninjas. Food analogy: sure, use a kitchen appliance, but please still remember how to use a knife, without cutting yourself.
Components are "objects connected by interfaces." (Jim Fulton) Resources are easier to work with when they are smaller and pluggable.
Use interfaces as schemas. For content items, you only want to add fields in this interface, and not methods: content should be simple. Then create adapters for adding functionality.
For debugging adapter lookup: temporarily move _zope_interface_coptimizations.so out of the zope.interface distributions. Restarts, take coffee, and debug. But this is not often needed.
Views. Other frameworks see views as endpoints for urls. We see views as all about content, using a multiadapter on the content and the request for it, which is terrific.
Some vocabularies may be static, but others need to be specific for a context, so we use the ZCA for that. Very useful.
For site-global utilities there are four ways:
- persistent component (utility). So this stores state in the ZODB. Uninstalling can be a nightmare, so watch out.
- adapter of a site. Needs to be cheap to construct, because you will construct this every time code needs it. You may still need to store state somewhere, like in an annotation or the configuration registry.
- plone.api in certain cases
- CMF tools (deprecated).
- Reg, from Martijn Faassen, inspired by zope.interface and zope.component
- Python's ABC, abstract base classes. But they are not really interfaces.