event_sourcery 0.13.0 → 0.14.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 5a53861c6b78ba25db797887cbb523bec700832a
4
- data.tar.gz: 2cc44cb2db162bf4b1f35e356e24eab1c213534d
3
+ metadata.gz: a6cabf34f27962b52571d80fb0a505186cef6e69
4
+ data.tar.gz: 3f07fcaf03c74190b2531099f1b4ca10d8312355
5
5
  SHA512:
6
- metadata.gz: 77e1852829109a6fcab8e4c2efb930de89b88dfa45eccbecf761484c7a19f6ab8825fd9179c9112002e6c82ab281033106dc0da60fad2f8be76dd97e0c42d782
7
- data.tar.gz: e0c6879eb49868144c628b6649c37d6577637f3514ef2653606a47d342cd820c9ed38f44588f77acd145f7b528a98b314a3c9189e27cd8f2971391c9ffe2978c
6
+ metadata.gz: 0a25a29c4d626ae5a179668b4663dc20dc81799948ee38506ce26b3e7b10d7c31e483baf8978507e9cb199b7eda616051b7d8c90e05b5d6af99713430a4fcb83
7
+ data.tar.gz: 334f2eb26deba53e6459c00caffec74403ee039fdee14d68c45243f889f64690d20082fb98e1006e50ceefaa647d8770090f9d25785b39ddbe9efe97bca01a67
data/CHANGELOG.md CHANGED
@@ -5,6 +5,16 @@ The format is based on [Keep a Changelog](http://keepachangelog.com/)
5
5
  and this project adheres to [Semantic Versioning](http://semver.org/).
6
6
 
7
7
  ## [Unreleased]
8
+
9
+ ## [0.14.0] - 2016-6-21
10
+ ### Added
11
+ - Added `Event#to_h` method. This returns a hash of the event attributes.
12
+ - Added `Event#with` method. This provides a way to create a new event
13
+ identical to the old event except for the provided changes.
14
+ - `Event#initialize` accepts `aggregate_id` parameter that either is
15
+ a strings or responds to `to_str`.
16
+
17
+ ## [0.13.0] - 2016-6-16
8
18
  ### Added
9
19
  - The core Event class accepts `causation_id` to allow event stores to
10
20
  add support for tracking causation ids with events.
data/README.md CHANGED
@@ -8,7 +8,8 @@ A framework for building event sourced, CQRS applications.
8
8
 
9
9
  - [Development Status](#development-status)
10
10
  - [Goals](#goals)
11
- - [Getting Started Guide](#getting-started-guide)
11
+ - [Related Repositories](#related-repositories)
12
+ - [Getting Started](#getting-started)
12
13
  - [Configuration](#configuration)
13
14
  - [Development](#development)
14
15
  - [Dependencies](#dependencies)
@@ -26,7 +27,7 @@ A framework for building event sourced, CQRS applications.
26
27
  - [Projectors](#projectors)
27
28
  - [Reactors](#reactors)
28
29
  - [Running Multiple ESPs](#running-multiple-esps)
29
- - [Typical Flow of State in an Event Sourcery Application](#typical-flow-of-state-in-an-event-sourcery-application)
30
+ - [Typical Flow of State in an EventSourcery Application](#typical-flow-of-state-in-an-eventsourcery-application)
30
31
  - [1. Handling a Command](#1-handling-a-command)
31
32
  - [2. Updating a Projection](#2-updating-a-projection)
32
33
  - [3. Handling a Query](#3-handling-a-query)
@@ -43,15 +44,21 @@ The goal of EventSourcery is to make it easier to build event sourced, CQRS appl
43
44
 
44
45
  The hope is that by using EventSourcery you can focus on modeling your domain with aggregates, commands, and events; and not worry about stitching together application plumbing.
45
46
 
46
- ## Getting Started Guide
47
+ ## Related Repositories
47
48
 
48
- EventSourcery currently supports a Postgres-based event store via the [event_sourcery-postgres](https://github.com/envato/event_sourcery-postgres) gem.
49
+ - EventSourcery's Postgres-based event store implementation: [event_sourcery-postgres](https://github.com/envato/event_sourcery-postgres).
50
+ - Example EventSourcery application: [event_sourcery_todo_app](https://github.com/envato/event_sourcery_todo_app).
51
+ - An opinionated CLI tool for building event sourced Ruby services with EventSourcery: [event_sourcery_generators](https://github.com/envato/event_sourcery_generators).
49
52
 
50
- **TODO**
53
+ ## Getting Started
54
+
55
+ The [example EventSourcery application](https://github.com/envato/event_sourcery_todo_app) is intended to illustrate concepts in EventSourcery, how they relate to each other, and how to use them in practice. If you'd like a succinct look at the library in practice take a look at that.
56
+
57
+ Otherwise you will generally need to add both event_sourcery and [event_sourcery-postgres](https://github.com/envato/event_sourcery-postgres) to your application.
51
58
 
52
59
  ## Configuration
53
60
 
54
- There are several ways to configure Event Sourcery to your liking. The following presents some examples:
61
+ There are several ways to configure EventSourcery to your liking. The following presents some examples:
55
62
 
56
63
  ```ruby
57
64
  EventSourcery.configure do |config|
@@ -59,7 +66,7 @@ EventSourcery.configure do |config|
59
66
  # One might set up Rollbar here.
60
67
  config.on_event_processor_error = proc { |exception, processor_name| … }
61
68
 
62
- # Enable Event Sourcery logging.
69
+ # Enable EventSourcery logging.
63
70
  config.logger = Logger.new('logs/my_event_sourcery_app.log')
64
71
 
65
72
  # Customize how event body attributes are serialized
@@ -162,7 +169,7 @@ Below is a high level view of a CQRS, event-sourced web application built using
162
169
 
163
170
  ### Events
164
171
 
165
- Events are value objects that record something of meaning in the domain. Think of a sequence of events as a time series of immutable domain facts.
172
+ Events are value objects that record something of meaning in the domain. Think of a sequence of events as a time series of immutable domain facts. Together they form the source of truth for our application's state.
166
173
 
167
174
  Events are targeted at an aggregate via an `aggregate_id` and have the following attributes.
168
175
 
@@ -235,7 +242,7 @@ A typical EventSourcery application will have one or more aggregate roots with m
235
242
 
236
243
  ### Event Processing
237
244
 
238
- A central part of EventSourcery is the processing of events in the store. Event Sourcery provides the Event Stream Processor abstraction to support this.
245
+ A central part of EventSourcery is the processing of events in the store. EventSourcery provides the Event Stream Processor abstraction to support this.
239
246
 
240
247
  ```
241
248
  ┌─────────────┐ Subscribe to the event store
@@ -255,7 +262,7 @@ store and/or triggering side └─────────────┘
255
262
  effects in the world.
256
263
  ```
257
264
 
258
- A typical Event Sourcery application will have multiple projectors and reactors running as background processes.
265
+ A typical EventSourcery application will have multiple projectors and reactors running as background processes.
259
266
 
260
267
  #### Event Stream Processors
261
268
 
@@ -279,15 +286,17 @@ A Reactor is an EventStreamProcessor that listens to events and emits events bac
279
286
 
280
287
  They typically record any external side effects they've triggered as events in the store.
281
288
 
289
+ Reactors can be used to build [process managers or sagas](https://msdn.microsoft.com/en-us/library/jj591569.aspx).
290
+
282
291
  #### Running Multiple ESPs
283
292
 
284
293
  An EventSourcery application will typically have multiple ESPs running. EventSourcery provides a class called [ESPRunner](lib/event_sourcery/event_processing/esp_runner.rb) which can be used to run ESPs. It runs each ESP in a forked child process so each ESP can process the event store independently. You can find an example in [event_sourcery_todo_app](https://github.com/envato/event_sourcery_todo_app/blob/master/Rakefile).
285
294
 
286
295
  Note that you may instead choose to run each ESP in their own process directly. The coordination of this is not currently provided by EventSourcery.
287
296
 
288
- ### Typical Flow of State in an Event Sourcery Application
297
+ ### Typical Flow of State in an EventSourcery Application
289
298
 
290
- Below we see the typical flow of state in an Event Sourcery application (arrows indicate data flow). Note that steps 1 and 2 are not synchronous. This means Event Sourcery applications need to embrace [eventual consistency](https://en.wikipedia.org/wiki/Eventual_consistency).
299
+ Below we see the typical flow of state in an EventSourcery application (arrows indicate data flow). Note that steps 1 and 2 are not synchronous. This means EventSourcery applications need to embrace [eventual consistency](https://en.wikipedia.org/wiki/Eventual_consistency).
291
300
 
292
301
  ```
293
302
 
@@ -351,7 +360,9 @@ end
351
360
 
352
361
  #### 2. Updating a Projection
353
362
 
354
- Projecting is process of converting (or collecting) a stream of events into a structural representation. You can think of the process as a fold over a sequence of events. You can think of a projection as a read model that is generally persisted somewhere like a database table.
363
+ You can think of projections as read-only models. They are created and updated by projectors and show different views over the events that are the source of truth for our application state. Projections are typically stored as database tables.
364
+
365
+ Projecting is process of converting (or collecting) a stream of events into these database tables. You can think of this process as a fold over a sequence of events.
355
366
 
356
367
  A projector is a process that listens for new events in the event store. When it sees a new event it cares about it updates its projection.
357
368
 
@@ -361,14 +372,14 @@ class OutstandingTodosProjector
361
372
 
362
373
  projector_name :outstanding_todos
363
374
 
364
- # Define our database table projection
375
+ # Database table that forms the projection.
365
376
  table :outstanding_todos do
366
377
  column :todo_id, 'UUID NOT NULL'
367
378
  column :title, :text
368
379
  column :description, :text
369
380
  end
370
381
 
371
- # Handle TodoAdded events by adding the todo to our projection
382
+ # Handle TodoAdded events by adding the todo to our projection.
372
383
  project TodoAdded do |event|
373
384
  table.insert(
374
385
  todo_id: event.aggregate_id,
@@ -377,7 +388,7 @@ class OutstandingTodosProjector
377
388
  )
378
389
  end
379
390
 
380
- # Handle TodoCompleted events by removing the todo from our projection
391
+ # Handle TodoCompleted events by removing the todo from our projection.
381
392
  project TodoCompleted, TodoAbandoned do |event|
382
393
  table.where(todo_id: event.aggregate_id).delete
383
394
  end
@@ -390,6 +401,7 @@ A query comes into the application and is routed to a query handler. The query h
390
401
 
391
402
  ```ruby
392
403
  module OutstandingTodos
404
+ # Query handler that queries the projection table.
393
405
  class QueryHandler
394
406
  def handle
395
407
  EventSourceryTodoApp.projections_database[:outstanding_todos].all
@@ -6,14 +6,14 @@ require 'event_sourcery/version'
6
6
  Gem::Specification.new do |spec|
7
7
  spec.name = 'event_sourcery'
8
8
  spec.version = EventSourcery::VERSION
9
- spec.authors = ['Steve Hodgkiss', 'Tao Guo', 'Sebastian von Conrad']
10
- spec.email = ['steve@hodgkiss.me', 'tao.guo@envato.com', 'sebastian.von.conrad@envato.com']
9
+ spec.authors = ['Envato']
10
+ spec.email = ['rubygems@envato.com']
11
11
 
12
12
  spec.summary = 'Event Sourcing Library'
13
13
  spec.description = ''
14
14
  spec.homepage = 'https://github.com/envato/event_sourcery'
15
15
 
16
- spec.files = `git ls-files -z`.split("\x0").reject { |f| f.match(%r{^(test|spec|features)/}) }
16
+ spec.files = `git ls-files -z`.split("\x0").reject { |f| f.match(%r{^(\.|Gemfile|Rakefile|bin/|script/|spec/)}) }
17
17
  spec.bindir = 'exe'
18
18
  spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
19
19
  spec.require_paths = ['lib']
@@ -21,7 +21,7 @@ module EventSourcery
21
21
  causation_id: nil)
22
22
  @id = id
23
23
  @uuid = uuid && uuid.downcase
24
- @aggregate_id = aggregate_id
24
+ @aggregate_id = aggregate_id && aggregate_id.to_str
25
25
  @type = self.class.type || type.to_s
26
26
  @body = body ? EventSourcery::EventBodySerializer.serialize(body) : {}
27
27
  @version = version ? Integer(version) : nil
@@ -45,5 +45,23 @@ module EventSourcery
45
45
  def <=>(other)
46
46
  id <=> other.id if other.is_a? Event
47
47
  end
48
+
49
+ def with(**attributes)
50
+ self.class.new(**to_h.merge!(attributes))
51
+ end
52
+
53
+ def to_h
54
+ {
55
+ id: id,
56
+ uuid: uuid,
57
+ aggregate_id: aggregate_id,
58
+ type: type,
59
+ body: body,
60
+ version: version,
61
+ created_at: created_at,
62
+ correlation_id: correlation_id,
63
+ causation_id: causation_id,
64
+ }
65
+ end
48
66
  end
49
67
  end
@@ -188,7 +188,7 @@ RSpec.shared_examples 'an event store' do
188
188
  RSpec.shared_examples 'gets events for a specific aggregate id' do
189
189
  before do
190
190
  event_store.sink(new_event(aggregate_id: aggregate_id, type: 'item_added', body: { 'my' => 'body' }))
191
- event_store.sink(new_event(aggregate_id: aggregate_id))
191
+ event_store.sink(new_event(aggregate_id: double(to_str: aggregate_id)))
192
192
  event_store.sink(new_event(aggregate_id: SecureRandom.uuid))
193
193
  end
194
194
 
@@ -1,3 +1,3 @@
1
1
  module EventSourcery
2
- VERSION = '0.13.0'.freeze
2
+ VERSION = '0.14.0'.freeze
3
3
  end
metadata CHANGED
@@ -1,16 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: event_sourcery
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.13.0
4
+ version: 0.14.0
5
5
  platform: ruby
6
6
  authors:
7
- - Steve Hodgkiss
8
- - Tao Guo
9
- - Sebastian von Conrad
7
+ - Envato
10
8
  autorequire:
11
9
  bindir: exe
12
10
  cert_chain: []
13
- date: 2017-06-16 00:00:00.000000000 Z
11
+ date: 2017-06-21 00:00:00.000000000 Z
14
12
  dependencies:
15
13
  - !ruby/object:Gem::Dependency
16
14
  name: bundler
@@ -84,24 +82,15 @@ dependencies:
84
82
  version: '0'
85
83
  description: ''
86
84
  email:
87
- - steve@hodgkiss.me
88
- - tao.guo@envato.com
89
- - sebastian.von.conrad@envato.com
85
+ - rubygems@envato.com
90
86
  executables: []
91
87
  extensions: []
92
88
  extra_rdoc_files: []
93
89
  files:
94
- - ".gitignore"
95
- - ".rspec"
96
- - ".travis.yml"
97
90
  - CHANGELOG.md
98
91
  - CODE_OF_CONDUCT.md
99
- - Gemfile
100
92
  - LICENSE.txt
101
93
  - README.md
102
- - Rakefile
103
- - bin/console
104
- - bin/setup
105
94
  - event_sourcery.gemspec
106
95
  - lib/event_sourcery.rb
107
96
  - lib/event_sourcery/aggregate_root.rb
data/.gitignore DELETED
@@ -1,37 +0,0 @@
1
- # Created by .ignore support plugin (hsz.mobi)
2
- ### Ruby template
3
- *.gem
4
- *.rbc
5
- /.config
6
- /coverage/
7
- /InstalledFiles
8
- /pkg/
9
- /spec/reports/
10
- /spec/examples.txt
11
- /test/tmp/
12
- /test/version_tmp/
13
- /tmp/
14
-
15
- # Used by dotenv library to load environment variables.
16
- # .env
17
-
18
- ## Documentation cache and generated files:
19
- /.yardoc/
20
- /_yardoc/
21
- /doc/
22
- /rdoc/
23
-
24
- ## Environment normalization:
25
- /.bundle/
26
- /vendor/bundle
27
- /lib/bundler/man/
28
-
29
- # for a library or gem, you might want to ignore these files since the code is
30
- # intended to run in multiple environments; otherwise, check them in:
31
- Gemfile.lock
32
- .ruby-version
33
- .ruby-gemset
34
-
35
- # unless supporting rvm < 1.11.0 or doing something fancy, ignore this:
36
- .rvmrc
37
-
data/.rspec DELETED
@@ -1,3 +0,0 @@
1
- --require spec_helper
2
- --format documentation
3
- --color
data/.travis.yml DELETED
@@ -1,8 +0,0 @@
1
- sudo: false
2
- language: ruby
3
- rvm:
4
- - 2.2
5
- - 2.3
6
- - 2.4
7
- before_install:
8
- - gem install bundler
data/Gemfile DELETED
@@ -1,5 +0,0 @@
1
- source 'https://rubygems.org'
2
-
3
- ruby '>= 2.2.0'
4
-
5
- gemspec
data/Rakefile DELETED
@@ -1,6 +0,0 @@
1
- require "bundler/gem_tasks"
2
- require "rspec/core/rake_task"
3
-
4
- RSpec::Core::RakeTask.new(:spec)
5
-
6
- task :default => :spec
data/bin/console DELETED
@@ -1,6 +0,0 @@
1
- #!/usr/bin/env ruby
2
-
3
- require "bundler/setup"
4
- require "event_sourcery"
5
- require "pry"
6
- Pry.start
data/bin/setup DELETED
@@ -1,15 +0,0 @@
1
- #!/bin/bash
2
- set -euo pipefail
3
- IFS=$'\n\t'
4
-
5
- echo
6
- echo "--- Bundling"
7
- echo
8
-
9
- bundle install
10
-
11
- echo
12
- echo "--- Preparing databases"
13
- echo
14
-
15
- createdb event_sourcery_test