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

Open
andrzejsliwa opened this Issue Sep 15, 2017 · 8 comments

Comments

3 participants
@andrzejsliwa
Member

andrzejsliwa commented Sep 15, 2017

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

This comment has been minimized.

Show comment
Hide comment
@andrzejsliwa

andrzejsliwa Sep 15, 2017

Member

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

Member

andrzejsliwa commented Sep 15, 2017

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

@paneq

This comment has been minimized.

Show comment
Hide comment
@paneq

paneq Sep 15, 2017

Member

@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?

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

This comment has been minimized.

Show comment
Hide comment
@paneq

paneq Sep 15, 2017

Member

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

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

This comment has been minimized.

Show comment
Hide comment
@paneq

paneq Sep 15, 2017

Member

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...

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

This comment has been minimized.

Show comment
Hide comment
@paneq

paneq Sep 15, 2017

Member

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
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

This comment has been minimized.

Show comment
Hide comment
@paneq

paneq Sep 15, 2017

Member

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
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

This comment has been minimized.

Show comment
Hide comment
@paneq

paneq Sep 15, 2017

Member

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
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 Sep 15, 2017

@paneq paneq reopened this Sep 15, 2017

@andrzejsliwa

This comment has been minimized.

Show comment
Hide comment
@andrzejsliwa

andrzejsliwa Sep 18, 2017

Member

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

Member

andrzejsliwa commented Sep 18, 2017

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

@pawelpacana pawelpacana added the idea label Oct 18, 2017

@pawelpacana pawelpacana added this to the post-1.0.0 milestone May 3, 2018

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment