@@ -75,10 +75,10 @@ class DogDetails(TypedDict):
7575 tricks: Tuple[str , ... ]
7676```
7777
78- Configure the application to use EventStoreDB by setting the application environment
79- variable ` PERSISTENCE_MODULE ` to ` 'eventsourcing_eventstoredb' ` . You can do this
80- in actual environment variables, by passing in an ` env ` argument when constructing
81- the application object, or by setting ` env ` on the application class.
78+ Configure the ` TrainingSchool ` application to use EventStoreDB by setting the
79+ environment variable ` PERSISTENCE_MODULE ` to ` 'eventsourcing_eventstoredb' ` . You
80+ can do this in actual environment variables, or by passing in an ` env ` argument when
81+ constructing the application object, or by setting ` env ` on the application class.
8282
8383``` python
8484import os
@@ -142,7 +142,7 @@ assert dog_details['name'] == 'Fido'
142142assert dog_details[' tricks' ] == (' roll over' , ' play dead' )
143143```
144144
145- ## Projections
145+ ## Eventually-consistent materialised views
146146
147147To project the state of an event-sourced application "write model" into a
148148materialised view "read model", first define an interface for the materialised view
@@ -155,7 +155,7 @@ application
155155from abc import abstractmethod
156156from eventsourcing.persistence import Tracking, TrackingRecorder
157157
158- class CountRecorderInterface (TrackingRecorder ):
158+ class MaterialisedViewInterface (TrackingRecorder ):
159159 @abstractmethod
160160 def incr_dog_counter (self , tracking : Tracking) -> None :
161161 pass
@@ -173,14 +173,14 @@ class CountRecorderInterface(TrackingRecorder):
173173 pass
174174```
175175
176- The ` CountRecorderInterface ` can be implemented to use a concrete database.
176+ The ` MaterialisedViewInterface ` can be implemented to use a concrete database.
177177
178178The example below counts dogs and tricks in memory, using "plain old Python objects".
179179
180180``` python
181181from eventsourcing.popo import POPOTrackingRecorder
182182
183- class POPOCountRecorder (POPOTrackingRecorder , CountRecorderInterface ):
183+ class InMemoryMaterialiseView (POPOTrackingRecorder , MaterialisedViewInterface ):
184184 def __init__ (self ):
185185 super ().__init__ ()
186186 self ._dog_counter = 0
@@ -203,15 +203,11 @@ class POPOCountRecorder(POPOTrackingRecorder, CountRecorderInterface):
203203 return self ._trick_counter
204204```
205205
206- After defining the materialised view interface, define how events will be processed
207- using the ` Projection ` class from the ` eventsourcing ` library.
206+ Define how events will be processed using the ` Projection ` class from the ` eventsourcing ` library.
208207
209208The example below processes ` Dog ` events. The ` Dog.Registered ` events are processed
210- by calling ` incr_dog_counter() ` . The ` Dog.TrickAdded ` events are processed by calling
211- ` incr_trick_counter() ` .
212-
213- Setting the event topics on the ` CountProjection ` class is not necessary, but speeds
214- event processing by filtering events in the application's database.
209+ by calling ` incr_dog_counter() ` on the materialised view. The ` Dog.TrickAdded ` events
210+ are processed by calling ` incr_trick_counter() ` .
215211
216212``` python
217213from eventsourcing.domain import DomainEventProtocol
@@ -220,36 +216,26 @@ from eventsourcing.projection import Projection
220216from eventsourcing.utils import get_topic
221217
222218
223- class CountProjection (Projection[CountRecorderInterface ]):
219+ class CountProjection (Projection[MaterialisedViewInterface ]):
224220 topics = (
225221 get_topic(Dog.Registered),
226222 get_topic(Dog.TrickAdded),
227223 )
228224
229- def __init__ (
230- self ,
231- tracking_recorder : CountRecorderInterface,
232- ):
233- assert isinstance (tracking_recorder, CountRecorderInterface), type (
234- tracking_recorder
235- )
236- super ().__init__ (tracking_recorder)
237-
238225 @singledispatchmethod
239226 def process_event (self , event : DomainEventProtocol, tracking : Tracking) -> None :
227+ pass
240228
241229 @process_event.register
242- def aggregate_created (self , event : Dog.Registered, tracking : Tracking) -> None :
243- self .tracking_recorder .incr_dog_counter(tracking)
230+ def dog_registered (self , event : Dog.Registered, tracking : Tracking) -> None :
231+ self .view .incr_dog_counter(tracking)
244232
245233 @process_event.register
246- def aggregate_event (self , event : Dog.TrickAdded, tracking : Tracking) -> None :
247- self .tracking_recorder .incr_trick_counter(tracking)
234+ def trick_added (self , event : Dog.TrickAdded, tracking : Tracking) -> None :
235+ self .view .incr_trick_counter(tracking)
248236```
249237
250- Run the projection with the ` ProjectionRunner ` class from the ` eventsourcing ` library,
251- by calling it with an application class, a projection class, and a concrete tracking
252- recorder class.
238+ Run the projection with the ` ProjectionRunner ` class from the ` eventsourcing ` library.
253239
254240The example below shows that when the projection is run, the materialised view is updated
255241by processing the event of the upstream event-sourced ` TrainingSchool ` application. It
@@ -264,17 +250,17 @@ from eventsourcing.projection import ProjectionRunner
264250with ProjectionRunner(
265251 application_class = TrainingSchool,
266252 projection_class = CountProjection,
267- tracking_recorder_class = POPOCountRecorder ,
253+ view_class = InMemoryMaterialiseView ,
268254) as runner:
269255
270256 # Get "read model" instance from runner, because
271257 # state of materialised view is stored in memory.
272- materialised_view = runner.projection.tracking_recorder
258+ materialised_view = runner.projection.view
273259
274260 # Wait for the existing events to be processed.
275261 materialised_view.wait(
276- training_school.name,
277- training_school.recorder.max_notification_id(),
262+ application_name = training_school.name,
263+ notification_id = training_school.recorder.max_notification_id(),
278264 )
279265
280266 # Query the "read model".
@@ -284,10 +270,10 @@ with ProjectionRunner(
284270 # Record another event in "write model".
285271 notification_id = training_school.add_trick(' Fido' , ' sit and stay' )
286272
287- # Wait for the new event to be processed by the projection .
273+ # Wait for the new event to be processed.
288274 materialised_view.wait(
289- training_school.name,
290- notification_id,
275+ application_name = training_school.name,
276+ notification_id = notification_id ,
291277 )
292278
293279 # Expect one trick more, same number of dogs.
@@ -308,10 +294,9 @@ with ProjectionRunner(
308294 assert trick_count + 2 == materialised_view.get_trick_counter()
309295```
310296
311- To implement a materialised view that uses PostgreSQL, please use the
312- ` PostgresTrackingRecorder ` class from the ` eventsourcing ` library (see
313- the library docs for more information about projecting the state of
314- an event-sourced application into PostgreSQL).
297+ See the Python ` eventsourcing ` package documentation for more information about
298+ projecting the state of an event-sourced application into materialised views
299+ that use a durable database such as SQLite and PostgreSQL.
315300
316301## More information
317302
0 commit comments