Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Discussion: Support for Refactoring use case for Aggregate #112

Closed
andrzejsliwa opened this issue Sep 15, 2017 · 9 comments
Closed

Discussion: Support for Refactoring use case for Aggregate #112

andrzejsliwa opened this issue Sep 15, 2017 · 9 comments

Comments

@andrzejsliwa
Copy link
Member

Theoretically can happen situation when you would like to move aggregate between namespaces or you choose wrong name for it. We don't want to modify our source of true (event store), but we are using aggregate names as part of identification of stream.

In such use case this example code lets to re-define name used for constructing stream id:

module Loans
  class Loan
    include Infra::AggregateRoot
    include ::Base::Contracts

    set_aggregate_name 'Loans::Loan'
    ...
  end
end
module Infra
  module AggregateRoot
    extend ActiveSupport::Concern

    included do
      include ::AggregateRoot
      extend AggregateName
    end

    module AggregateName
      # rubocop:disable Style/AccessorMethodName
      def set_aggregate_name(aggregate_name)
        @aggregate_name = aggregate_name
      end

      def aggregate_name
        @aggregate_name
      end
    end
  end
end
module Infra
  module WithAggregate
    include Contracts::Core
    include Contracts::Builtin

    private

    def with_aggregate(aggregate_class, aggregate_id, **args)
      aggregate = build(aggregate_class, aggregate_id, **args)
      yield aggregate
      aggregate.store
      aggregate
    end

    def build(aggregate_class, aggregate_id, **args)
      name = aggregate_class.aggregate_name || aggregate_class.name
      stream = "#{name}$#{aggregate_id}"
      aggregate_class.new(aggregate_id: aggregate_id, **args)
        .load(stream, event_store: Rails.configuration.core.event_store)
    end
  end
end
@andrzejsliwa
Copy link
Member Author

I would like to discuss here how to apply this or similar design to base gem.

@paneq
Copy link
Member

paneq commented Sep 15, 2017

@andrzejsliwa my feeling is that there is no need to support it in rails_event_store as the responsibility for providing stream name is already fully on the side of application code. So whatever convention you decided upon, it is also your code that can easily change the convention to a different one.

What do you think?

@paneq
Copy link
Member

paneq commented Sep 15, 2017

Something like this:

    def with_aggregate(aggregate_class, aggregate_id, **args)
      aggregate = build(aggregate_class, aggregate_id, **args)
      yield aggregate
      aggregate.store
      aggregate
    end

could be useful in AggregateRoot gem

@paneq
Copy link
Member

paneq commented Sep 15, 2017

Maybe in the style of

p = Product.new
p.update(stream_name) do
  p.reserve(quantity: 30)
end

and that would do load and store internally...

@paneq
Copy link
Member

paneq commented Sep 15, 2017

Then for convention one could overwrite update to do

module AutoStreamName
  def update(id, &block)
    super([self.class.aggregate_name, id].join("$"), &block)
  end

  module Name
    def aggregate_name
      @aggregate_name ||= self.class.name
    end

    def aggregate_name=(val)
      @aggregate_name = val
    end
  end
  included{ extend Name }
end
class Product
  include AggregateRoot
  include AutoStreamName
end
class Order
  include AggregateRoot
  include AutoStreamName

  self.aggregate_name = "Bakset"
end

@paneq
Copy link
Member

paneq commented Sep 15, 2017

Or:

module AutoStreamName
  def update(id, &block)
    super([aggregate_name, id].join("$"), &block)
  end

  def aggregate_name
    self.class.name
  end
end
class Product
  include AggregateRoot
  include AutoStreamName
end
class Order
  include AggregateRoot
  include AutoStreamName

  private

  def aggregate_name
    "Basket"
  end
end

@paneq
Copy link
Member

paneq commented Sep 15, 2017

I can imagine AutoStreamName being part of AggregateRoot but not included by default. Better name needed.

I imagine it could overwrite load, store, update (not currently present) to use stream_name_prefix + id (provided instead of stream_name) to compute stream_name and call super.

AR method:

def load(stream_name, event_store: default_event_store)
end

AutoStreamName method:

def load(id, event_store: default_event_store)
  super([aggregate_name, id].join("$"), event_store: event_store)
end

I am not sure if aggregate_name should be defined as instance method or a class method.

I also wonder if maybe Id could come from the object itself.

module AutoStreamName
  def load(event_store: default_event_store)
    super([aggregate_name, id].join("$"), event_store: event_store)
  end
end

class Product
  attr_reader :id

  include AggregateRoot
  include AutoStreamName

  def initialize(id)
    @id = id
  end

@paneq paneq closed this as completed Sep 15, 2017
@paneq paneq reopened this Sep 15, 2017
@andrzejsliwa
Copy link
Member Author

I like idea of AutoStreamName I like declarative form of it, I don't see any big tradeoffs of using it and having it by default included in aggregate. Keeping update method in this form will also streamline stream naming in all projects which are using RailsEventStore

@mostlyobvious mostlyobvious added this to the post-1.0.0 milestone May 3, 2018
@mpraglowski
Copy link
Member

@andrzejsliwa @paneq this discussion is no longer valid - aggregate class does not define a stream name anymore, load & store operations have been moved to AggregateRoot::Repository when you explicit give a stream name as argument. See #547

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

4 participants