Skip to content

Commit

Permalink
Improved infrastructure documentation.
Browse files Browse the repository at this point in the history
  • Loading branch information
johnbywater committed Dec 13, 2017
1 parent 441b8e9 commit 1f79233
Showing 1 changed file with 100 additions and 61 deletions.
161 changes: 100 additions & 61 deletions docs/topics/infrastructure.rst
Original file line number Diff line number Diff line change
Expand Up @@ -150,6 +150,7 @@ The library has a concrete active record strategy for SQLAlchemy provided by the
The library also provides active record classes for SQLAlchemy, such as ``IntegerSequencedItemRecord`` and
``StoredEventRecord``. The ``IntegerSequencedItemRecord`` class matches the default ``SequencedItem``
namedtuple. The ``StoredEventRecord`` class matches the alternative ``StoredEvent`` namedtuple.
There is also a ``TimestampSequencedItemRecord`` and a ``SnapshotRecord``.

The code below uses the namedtuple ``StoredEvent`` and the active record ``StoredEventRecord``.

Expand Down Expand Up @@ -206,6 +207,9 @@ requires a scoped session object, passed using the constructor arg ``session``.
wish to use a different scoped session facade, such as a request-scoped session object provided by a Web
framework.

With the database setup, the ``SQLAlchemyActiveRecordStrategy`` can be constructed,
and used to store events using SQLAlchemy.


.. code:: python
Expand Down Expand Up @@ -246,6 +250,14 @@ Since by now only one item was stored, so there is only one item in the results.
assert len(results) == 1
assert results[0] == stored_event1
SQLAlchemy Dialects
~~~~~~~~~~~~~~~~~~~

The databases supported by core `SQLAlchemy dialects <http://docs.sqlalchemy.org/en/latest/dialects/>`
are Firebird, Microsoft SQL Server, MySQL, Oracle, PostgreSQL, SQLite, and Sybase. This library's
infrastructure classes for SQLAlchemy have been tested with MySQL, PostgreSQL, and SQLite.

MySQL
~~~~~

Expand Down Expand Up @@ -280,62 +292,28 @@ The ``uri`` for PostgreSQL would look something like this.
postgresql://username:password@localhost:5432/eventsourcing
Apache Cassandra
----------------

To run the examples below, please install the library with the
'cassandra' option.

.. code::
$ pip install eventsourcing[cassandra]
The library also has a concrete active record strategy for Apache Cassandra provided by
``CassandraActiveRecordStrategy`` class.

Similarly, for the ``CassandraActiveRecordStrategy``, the ``IntegerSequencedItemRecord``
from ``eventsourcing.infrastructure.cassandra.activerecords`` matches the ``SequencedItem``
namedtuple. The ``StoredEventRecord`` from the same module matches the ``StoredEvent``
namedtuple. There is also a ``TimestampSequencedItemRecord`` and a ``SnapshotRecord``.

.. code:: python
from eventsourcing.infrastructure.cassandra.datastore import CassandraDatastore, CassandraSettings
from eventsourcing.infrastructure.cassandra.activerecords import CassandraActiveRecordStrategy, StoredEventRecord
cassandra_datastore = CassandraDatastore(
settings=CassandraSettings(),
tables=(StoredEventRecord,)
)
cassandra_datastore.setup_connection()
cassandra_datastore.setup_tables()
cassandra_active_record_strategy = CassandraActiveRecordStrategy(
active_record_class=StoredEventRecord,
sequenced_item_class=StoredEvent,
)
SQLite
~~~~~~

results = cassandra_active_record_strategy.list_items(aggregate1)
assert len(results) == 0
SQLite is shipped with core Python packages, so nothing extra needs to be installed.

cassandra_active_record_strategy.append(stored_event1)
The ``uri`` for a temporary SQLite database might look something like this.

results = cassandra_active_record_strategy.list_items(aggregate1)
assert results[0] == stored_event1
.. code::
cassandra_datastore.drop_tables()
cassandra_datastore.close_connection()
sqlite:::////tmp/eventsourcing.db
The ``CassandraDatastore`` and ``CassandraSettings`` are be used in the same was as
``SQLAlchemyDatastore`` and ``SQLAlchemySettings`` above. Please investigate
library class :class:`~eventsourcing.infrastructure.cassandra.datastore.CassandraSettings`
for information about configuring away from default settings.
Please note, the library's SQLAlchemy insfrastructure defaults to using
an in memory SQLite database, which is the fastest way to run the library,
and is recommended as a convenience for development.


Django ORM
----------------
----------

The library also has a concrete active record strategy for the Django ORM provided by
``DjangoActiveRecordStrategy`` class.

To run the examples below, please install the library with the
'django' option.
Expand All @@ -345,10 +323,7 @@ To run the examples below, please install the library with the
$ pip install eventsourcing[django]
The library also has a concrete active record strategy for the Django ORM provided by
``DjangoActiveRecordStrategy`` class.

Similarly, for the ``DjangoActiveRecordStrategy``, the ``IntegerSequencedItemRecord``
For the ``DjangoActiveRecordStrategy``, the ``IntegerSequencedItemRecord``
from ``eventsourcing.infrastructure.django.models`` matches the ``SequencedItem``
namedtuple. The ``StoredEventRecord`` from the same module matches the ``StoredEvent``
namedtuple. There is also a ``TimestampSequencedItemRecord`` and a ``SnapshotRecord``.
Expand All @@ -370,7 +345,7 @@ of ``INSTALLED_APPS``, or import the classes you want into one of your applicati
]
The library has a little Django project, used for testing. We can used it here for demonstration purposes.
The library has a little Django project for testing the app, it used here to help run the app.

.. code:: python
Expand Down Expand Up @@ -407,6 +382,9 @@ An alternative to using the ``manage.py`` command line interface is using the
call_command('migrate')
With the database setup, the ``DjangoActiveRecordStrategy`` can be constructed,
and used to store events using the Django ORM.

.. code:: python
from eventsourcing.infrastructure.django.activerecords import DjangoActiveRecordStrategy
Expand All @@ -428,8 +406,8 @@ An alternative to using the ``manage.py`` command line interface is using the
Please note, if you want to use the Django ORM as infrastructure for
an event sourced application, the you can develop an application in
the normal wasy described in other sections of this documentation.
an event sourced application, the you can use the application classes
in the :doc:`application </topcis/application>` section of this documentation.

When it comes to deployment, just remember that you need only one
instance of the application in any given process, otherwise subscribers
Expand All @@ -446,7 +424,7 @@ necessary, otherwise for the whole suite. Tests can then get the application
object freely.

For the second case, it is recommended to construct the application object from
the wsgi.py file, which doesn't get used when running Django from a test suite,
the project's ``wsgi.py`` file, which doesn't get used when running Django from a test suite,
or from a task queue worker. Views can then get the application object freely.
Closing the application doesn't matter, because it will be used until the process
ends.
Expand All @@ -462,16 +440,77 @@ the application object with a module level function called ``init_application()`
that assigns to a module level variable, and then obtain the application object with
another module level function called ``get_application()``, which raises an exception
if the application has not been constructed. See the
:doc:`deployment </topics/infrastructure>` section for more information.
:doc:`deployment </topics/deployment>` section for more information.


Django Backends
~~~~~~~~~~~~~~~

The supported `Django backends <https://docs.djangoproject.com/en/2.0/ref/databases/>`
are PostgreSQL, MySQL, SQLite, and Oracle. This library's Django infrastructure classes
have been tested with PostgreSQL, MySQL, SQLite.


Apache Cassandra
----------------

To run the examples below, please install the library with the
'cassandra' option.

.. code::
$ pip install eventsourcing[cassandra]
The library also has a concrete active record strategy for Apache Cassandra provided by
``CassandraActiveRecordStrategy`` class.

For the ``CassandraActiveRecordStrategy``, the ``IntegerSequencedItemRecord``
from ``eventsourcing.infrastructure.cassandra.activerecords`` matches the ``SequencedItem``
namedtuple. The ``StoredEventRecord`` from the same module matches the ``StoredEvent``
namedtuple. There is also a ``TimestampSequencedItemRecord`` and a ``SnapshotRecord``.

.. code:: python
from eventsourcing.infrastructure.cassandra.datastore import CassandraDatastore, CassandraSettings
from eventsourcing.infrastructure.cassandra.activerecords import CassandraActiveRecordStrategy, StoredEventRecord
cassandra_datastore = CassandraDatastore(
settings=CassandraSettings(),
tables=(StoredEventRecord,)
)
cassandra_datastore.setup_connection()
cassandra_datastore.setup_tables()
cassandra_active_record_strategy = CassandraActiveRecordStrategy(
active_record_class=StoredEventRecord,
sequenced_item_class=StoredEvent,
)
results = cassandra_active_record_strategy.list_items(aggregate1)
assert len(results) == 0
cassandra_active_record_strategy.append(stored_event1)
results = cassandra_active_record_strategy.list_items(aggregate1)
assert results[0] == stored_event1
cassandra_datastore.drop_tables()
cassandra_datastore.close_connection()
The ``CassandraDatastore`` and ``CassandraSettings`` are be used in the same was as
``SQLAlchemyDatastore`` and ``SQLAlchemySettings`` above. Please investigate
library class :class:`~eventsourcing.infrastructure.cassandra.datastore.CassandraSettings`
for information about configuring away from default settings.


Sequenced item conflicts
------------------------

It is a feature of the active record strategy that it isn't possible successfully to append two items at the same
position in the same sequence. If such an attempt is made, a ``SequencedItemConflict`` will be raised by the active
record strategy.

It is a common feature of the active record strategy classes that it isn't possible successfully
to append two items at the same position in the same sequence. If such an attempt is made, a
``SequencedItemConflict`` will be raised.

.. code:: python
Expand All @@ -488,6 +527,7 @@ record strategy.
This feature is implemented using optimistic concurrency control features of the underlying database. With
SQLAlchemy, a unique constraint is used that involves both the sequence and the position columns.
The Django ORM strategy works in the same way.

With Cassandra the position is the primary key in the sequence partition, and the "IF NOT
EXISTS" feature is applied. The Cassandra database management system implements the Paxos
Expand All @@ -497,7 +537,6 @@ being distributed. It is also possible to serialize calls to the methods of an e
that is out of the scope of this package — if you wish to do that, perhaps something like
`Zookeeper <https://zookeeper.apache.org/>`__ might help.

The Django ORM strategy works in the same way as the SQLAlchemy strategy.


Sequenced item mapper
Expand Down

0 comments on commit 1f79233

Please sign in to comment.