Class: Yes::Core::Aggregate
- Inherits:
-
Object
- Object
- Yes::Core::Aggregate
- Includes:
- Draftable, HasAuthorizer, HasReadModel
- Defined in:
- lib/yes/core/aggregate.rb,
lib/yes/core/aggregate/draftable.rb,
lib/yes/core/aggregate/has_authorizer.rb,
lib/yes/core/aggregate/has_read_model.rb,
lib/yes/core/aggregate/dsl/command_data.rb,
lib/yes/core/aggregate/dsl/attribute_data.rb,
lib/yes/core/aggregate/dsl/command_definer.rb,
lib/yes/core/aggregate/read_model_rebuilder.rb,
lib/yes/core/aggregate/dsl/attribute_definer.rb,
lib/yes/core/aggregate/dsl/constant_resolver.rb,
lib/yes/core/aggregate/dsl/class_resolvers/base.rb,
lib/yes/core/aggregate/dsl/class_name_convention.rb,
lib/yes/core/aggregate/shared_read_model_rebuilder.rb,
lib/yes/core/aggregate/dsl/command_shortcut_expander.rb,
lib/yes/core/aggregate/dsl/class_resolvers/authorizer.rb,
lib/yes/core/aggregate/dsl/class_resolvers/read_model.rb,
lib/yes/core/aggregate/dsl/attribute_definers/standard.rb,
lib/yes/core/aggregate/dsl/attribute_definers/aggregate.rb,
lib/yes/core/aggregate/dsl/class_resolvers/command/base.rb,
lib/yes/core/aggregate/dsl/method_definers/command/base.rb,
lib/yes/core/aggregate/dsl/class_resolvers/command/event.rb,
lib/yes/core/aggregate/dsl/method_definers/attribute/base.rb,
lib/yes/core/aggregate/dsl/class_resolvers/command/command.rb,
lib/yes/core/aggregate/dsl/method_definers/command/command.rb,
lib/yes/core/aggregate/dsl/class_resolvers/read_model_filter.rb,
lib/yes/core/aggregate/dsl/class_resolvers/command/authorizer.rb,
lib/yes/core/aggregate/dsl/method_definers/attribute/accessor.rb,
lib/yes/core/aggregate/dsl/method_definers/command/can_command.rb,
lib/yes/core/aggregate/dsl/class_resolvers/command/state_updater.rb,
lib/yes/core/aggregate/dsl/class_resolvers/read_model_serializer.rb,
lib/yes/core/aggregate/dsl/class_resolvers/command/guard_evaluator.rb,
lib/yes/core/aggregate/dsl/class_resolvers/command/cerbos_authorizer.rb,
lib/yes/core/aggregate/dsl/class_resolvers/command/simple_authorizer.rb,
lib/yes/core/aggregate/dsl/class_resolvers/command/authorizer_factory.rb,
lib/yes/core/aggregate/dsl/method_definers/attribute/aggregate_accessor.rb
Overview
The Aggregate class represents a core entity in the eventsourcing system. It provides functionality for managing event sourcing patterns including:
-
Attribute management with automatic command and event generation
-
Parent-child aggregate relationships
-
Read model associations
-
Context management
Defined Under Namespace
Modules: Draftable, Dsl, HasAuthorizer, HasReadModel Classes: ReadModelRebuilder, SharedReadModelRebuilder
Class Attribute Summary collapse
-
._primary_context ⇒ String?
readonly
The primary context name for this aggregate.
Instance Attribute Summary collapse
- #id ⇒ Object readonly
Class Method Summary collapse
-
.aggregate ⇒ String
Returns the aggregate name without namespace and “Aggregate” suffix.
-
.attribute(name, type, **options) { ... } ⇒ Object
Defines an attribute on the aggregate which creates corresponding command, event and handler.
-
.attribute_options ⇒ Hash
The attribute options (localized, encrypted, etc.).
-
.attributes ⇒ Hash
The attributes defined on this aggregate.
-
.command(*args) ⇒ Object
Defines a command on the aggregate which creates corresponding command and event classes.
-
.commands ⇒ Hash
The commands defined on this aggregate.
-
.context ⇒ String
Returns the context namespace for the aggregate.
-
.inherited(subclass) ⇒ void
Hook that runs when a class inherits from Aggregate.
-
.parent(name, **options) { ... } ⇒ void
Defines a parent aggregate and automatically registers a corresponding Assign command together with a corresponding attribute.
-
.parent_aggregates ⇒ Hash<Symbol, Hash>
Retrieves or initializes the parent_aggregates hash.
-
.primary_context(context) ⇒ void
Sets the primary context for the aggregate.
-
.removable(attr_name: :removed_at) { ... } ⇒ void
Defines a default removal behavior for the aggregate.
Instance Method Summary collapse
-
#commands ⇒ Hash<Symbol, Array<Symbol>>
Returns a list of commands that can be executed on this aggregate with their associated events.
-
#event_revision ⇒ Integer
Returns the stream revision number of the latest event.
-
#events ⇒ Enumerator<PgEventstore::Event>
Returns the events for the aggregate.
-
#initialize(id = SecureRandom.uuid, draft: false) ⇒ Yes::Core::Aggregate
constructor
Initializes a new aggregate instance.
-
#latest_event ⇒ PgEventstore::Event?
Retrieves the most recent event from the aggregate’s event stream.
-
#reload ⇒ Yes::Core::Aggregate
Reloads the aggregate and its read model.
Methods included from Draftable
#draft?, #read_model, #update_read_model
Methods included from HasReadModel
#init_revision_from_stream, #read_model, #rebuild_read_model, #remove_read_model, #revision, #revision_column, #update_read_model
Constructor Details
#initialize(id = SecureRandom.uuid, draft: false) ⇒ Yes::Core::Aggregate
Initializes a new aggregate instance
327 328 329 330 331 332 333 334 335 336 337 338 |
# File 'lib/yes/core/aggregate.rb', line 327 def initialize(id = SecureRandom.uuid, draft: false) validate_draft_initialization(draft) @id = id @draft = draft @command_utilities = Utils::CommandUtils.new( context: self.class.context, aggregate: self.class.aggregate, aggregate_id: @id ) end |
Class Attribute Details
._primary_context ⇒ String? (readonly)
Returns The primary context name for this aggregate.
54 55 56 |
# File 'lib/yes/core/aggregate.rb', line 54 def _primary_context @_primary_context end |
Instance Attribute Details
#id ⇒ Object (readonly)
44 45 46 |
# File 'lib/yes/core/aggregate.rb', line 44 def id @id end |
Class Method Details
.aggregate ⇒ String
Returns the aggregate name without namespace and “Aggregate” suffix
267 268 269 |
# File 'lib/yes/core/aggregate.rb', line 267 def aggregate name.to_s.split('::')[-2] end |
.attribute(name, type, **options) { ... } ⇒ Object
Defines an attribute on the aggregate which creates corresponding command, event and handler
171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 |
# File 'lib/yes/core/aggregate.rb', line 171 def attribute(name, type, **, &) raise 'Aggregate attribute definition with command: true is not allowed' if type == :aggregate && [:command] @attributes ||= {} @attributes[name] = type @attribute_options ||= {} @attribute_options[name] = .slice(:localized) = .merge(context:, aggregate:) Dsl::AttributeDefiner.new( Dsl::AttributeData.new(name, type, self, ) ).call command(:change, name, type, &) if [:command] end |
.attribute_options ⇒ Hash
Returns The attribute options (localized, encrypted, etc.).
277 278 279 |
# File 'lib/yes/core/aggregate.rb', line 277 def @attribute_options ||= {} end |
.attributes ⇒ Hash
Returns The attributes defined on this aggregate.
272 273 274 |
# File 'lib/yes/core/aggregate.rb', line 272 def attributes @attributes ||= {} end |
.command(name) { ... } ⇒ Object .command(publish) ⇒ void .command(change, attribute, **options) ⇒ void .command(enable, attribute, **options) ⇒ void .command(toggle_names, attribute) ⇒ void
Defines a command on the aggregate which creates corresponding command and event classes
242 243 244 245 246 247 248 249 250 251 |
# File 'lib/yes/core/aggregate.rb', line 242 def command(*args, **, &) return handle_command_shortcut(*args, **, &) unless Dsl::CommandShortcutExpander.base_case?(*args, **, &) name = args.first @commands ||= {} command_data = Dsl::CommandData.new(name, self, { context:, aggregate: }) @commands[name] = command_data Dsl::CommandDefiner.new(command_data).call(&) end |
.commands ⇒ Hash
Returns The commands defined on this aggregate.
282 283 284 |
# File 'lib/yes/core/aggregate.rb', line 282 def commands @commands ||= {} end |
.context ⇒ String
Returns the context namespace for the aggregate
258 259 260 |
# File 'lib/yes/core/aggregate.rb', line 258 def context name.to_s.split('::').first end |
.inherited(subclass) ⇒ void
This method returns an undefined value.
Hook that runs when a class inherits from Aggregate
59 60 61 62 63 64 65 66 67 68 69 70 71 72 |
# File 'lib/yes/core/aggregate.rb', line 59 def inherited(subclass) super # Add an "end of definition" hook using at_exit # Setting up read model classes is done here, because it needs to be done after # the class definition is complete. TracePoint.new(:end) do |tp| if tp.self == subclass subclass.setup_read_model_classes if subclass.read_model_enabled? subclass. tp.disable end end.enable end |
.parent(name, **options) { ... } ⇒ void
This method returns an undefined value.
Defines a parent aggregate and automatically registers a corresponding Assign command together with a corresponding attribute.
81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 |
# File 'lib/yes/core/aggregate.rb', line 81 def parent(name, **, &) parent_aggregates[name] = attribute name, :aggregate return unless .fetch(:command, true) command :"assign_#{name}" do payload "#{name}_id": :uuid guard(:no_change) { public_send(:"#{name}_id") != payload.public_send(:"#{name}_id") } instance_eval(&) if block_given? end end |
.parent_aggregates ⇒ Hash<Symbol, Hash>
Retrieves or initializes the parent_aggregates hash.
100 101 102 |
# File 'lib/yes/core/aggregate.rb', line 100 def parent_aggregates @parent_aggregates ||= {} end |
.primary_context(context) ⇒ void
This method returns an undefined value.
Sets the primary context for the aggregate.
141 142 143 |
# File 'lib/yes/core/aggregate.rb', line 141 def primary_context(context) @_primary_context = context end |
.removable(attr_name: :removed_at) { ... } ⇒ void
This method returns an undefined value.
Defines a default removal behavior for the aggregate.
127 128 129 130 131 132 133 134 135 |
# File 'lib/yes/core/aggregate.rb', line 127 def removable(attr_name: :removed_at, &) attribute attr_name, :datetime unless attributes.key?(attr_name) command :remove do guard(:no_change) { !public_send(attr_name) } update_state { method(attr_name).call { Time.current } } instance_eval(&) if block_given? end end |
Instance Method Details
#commands ⇒ Hash<Symbol, Array<Symbol>>
Returns a list of commands that can be executed on this aggregate with their associated events
381 382 383 384 385 386 387 388 |
# File 'lib/yes/core/aggregate.rb', line 381 def commands mappings = Yes::Core.configuration.command_event_mappings( self.class.context, self.class.aggregate ) mappings.sort.to_h end |
#event_revision ⇒ Integer
Returns the stream revision number of the latest event
367 368 369 |
# File 'lib/yes/core/aggregate.rb', line 367 def event_revision latest_event.stream_revision end |
#events ⇒ Enumerator<PgEventstore::Event>
Returns the events for the aggregate
350 351 352 353 354 |
# File 'lib/yes/core/aggregate.rb', line 350 def events PgEventstore.client.read_paginated( command_utilities.build_stream(metadata: { draft: draft? }), options: { direction: 'Forwards' } ) end |
#latest_event ⇒ PgEventstore::Event?
Retrieves the most recent event from the aggregate’s event stream
358 359 360 361 362 |
# File 'lib/yes/core/aggregate.rb', line 358 def latest_event PgEventstore.client.read( command_utilities.build_stream(metadata: { draft: draft? }), options: { max_count: 1, direction: :desc } ).first end |
#reload ⇒ Yes::Core::Aggregate
Reloads the aggregate and its read model
342 343 344 345 346 |
# File 'lib/yes/core/aggregate.rb', line 342 def reload read_model&.reload self end |