[annotator-dev] Annotator architecture evolution

Nick Stenning nick at whiteink.com
Mon Jun 24 16:04:52 UTC 2013

Dear Annotators,

Over the past few months, I've been discussing the future of Annotator's
APIs with those of you I've met in person at conferences and cafés, or
online in #annotator and elsewhere. It's time to bring those discussions to
the wider community on this mailing list for review.

My primary concern is making integrators' lives easier.

Specifically, that means looking at how people currently use Annotator, and
how with appropriate changes to the internal architecture they would be
able to work faster and more productively.

This is a long email, but if you are working with Annotator on a daily
basis I'd really appreciate feedback, in particular on the first section,
which attempts to lay out the current architectural flaws in Annotator.

---- ---- ---- ---- ---- ----

# What's wrong right now?

## UI coupling

Implementing your own UI is difficult. The current highlighter, editor and
viewer are quite tightly bound to core Annotator and this has made life
difficult for Hypothesis, AustESE, and others.

## Storage coupling

The Store plugin is rather special, but this is implicit. It doesn't
implement a different plugin API to any other plugin, but rather hooks into
the existing annotationCreated/annotationUpdated events raised by core

On one level, this could be seen as a good thing, but my feeling is that it
substantially raises the complexity of Annotator as a whole. In particular,
storage plugins are the only plugins on which most of the rest of Annotator
depends, and in which success or failure of function calls may be entirely
outside the control of the client side code. The process by which the
results of these calls are communicated to the rest of Annotator and
plugins is currently mostly magic.

We need to make the privileged nature of storage plugins explicit, and
implement a clear mechanism for constructing and loading annotations
asynchronously, so that other plugins don't have to be exposed to the
complexity of the storage implementation.

## Inflexible matching

Annotator only supports one kind of selector by default, the
XPointer/TextPositionSelector method that it's used since day one.

We need to provide native support for matcher plugins, which receive
annotations at load time, add arbitrary data to those annotations (in the
XPointer case, this might be an array of DOM textNodes) which can be used
by view plugins (e.g. Highlighter, Viewer, etc.).

This data wouldn't be serialized and as such we need a more general pattern
for attaching local data to annotations. Something better than



## Interplugin communication more generally

There are a couple of places where one plugin relies on the actions of
another plugin. In some places this isn't done in a general or flexible
way, e.g.


In other places, this integration is direct between plugins and almost



Wherever possible, this kind of integration should be mediated through a
common piece of infrastructure, namely Annotator core.

There's scope here to clean up some of the existing event naming, too.

## Removal and idempotence

Removing and reinstantiating an Annotator instance is currently quite
tiresome. We need a generic way of blatting existing Annotator instances
and replacing them with new ones.

---- ---- ---- ---- ---- ----

And now, proposals for improvement:

---- ---- ---- ---- ---- ----

## Plugin types

As I see it, there are essentially three types of plugin:

1. Generic plugins

A generic plugin can subscribe to Annotator events, such as annotation
creation, update, or deletion. They might bind their own events to the DOM
to present UI to create, update, or delete annotations. Also included might
be plugins that inform the user of error conditions, manage auth tokens,

2. Store plugins

Store plugins implement a common interface to CRUD and query operations on
backend data stores. Each operation might succeed or fail due to model
constraints, network outage, etc. A minimal interface might be:

  create :: obj -> Promise(ann)
  update :: ann -> Promise(ann)
  delete :: ann -> Promise(None)
  query :: id -> Promise(([ann], queryMeta))

Store plugins are managed by a new concept in Annotator-land, the registry.
The registry is responsible for abstracting other plugins and users from
the details of any particular Store plugin, for error handling, and for
integration with Matcher plugins (see below).

Critically, the registry can also receive events from the Store plugin,
which it will then distribute to observers (generic plugins). This gives us
a relatively sane mechanism for implementing Store plugins which stream
data from the backend store (i.e. realtime collaboration).

Only one store plugin will be active at any one time.

3. Matcher plugins

These are plugins that take annotations from their serialized form, and use
the current execution context (including, say, the current state of the
DOM) to add additional data to the annotations which can be used by generic
plugins to identify and draw annotations on the page.

## Asynchrony

Things that need to be done asynchronously return Promises. 'Nuff said.

---- ---- ---- ---- ---- ----

I reckon that making clear the different purposes of the different plugins
and extracting the stuff that is currently in Annotator core into
appropriate plugins will solve most of the issues people have encountered
integrating with Annotator.

This is, however, a big chunk of work, and I don't think it's going to be
possible to do so without breaking some existing integrations. While
obviously we should try and minimise breakage, I think it's possible that
the result of some of this work will become Annotator v2.0.0 (for an
explanation of why, see http://semver.org/).

Thanks for reading this far!

-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://lists.okfn.org/pipermail/annotator-dev/attachments/20130624/317afc47/attachment-0001.html>

More information about the annotator-dev mailing list