RailsEventStore / rails_event_store

A Ruby implementation of an Event Store based on Active Record
http://railseventstore.org
MIT License
1.42k stars 121 forks source link

bounded_context:bounded_context generator output path #186

Closed andriytyurnikov closed 6 years ago

andriytyurnikov commented 6 years ago

Running rails g bounded_context:bounded_context MyContext creates files right in project's root. Is it by design?

paneq commented 6 years ago

@andriytyurnikov yes. Have you experienced any issue with that?

andriytyurnikov commented 6 years ago

@paneq, well, no, just got surprised with it, as touching root directory is quite unusual for rails generators and tools. Thanks for clarification

andriytyurnikov commented 6 years ago

@paneq , I also wonder if running rails test would run tests of bounded context, and how it interacts with general rails tools and scripts

andrzejkrzywda commented 6 years ago

@andriytyurnikov Thanks for trying the generator! Just to make sure - what files did it create for you?

andriytyurnikov commented 6 years ago

@andrzejkrzywda after running rails g bounded_context:bounded_context PlatformCore I've got platform_core/lib/platform_core.rb and platform_core/test/test_helper.rb in a root folder, not in app/ or lib/ or in test/ - just in the root. Which is fairly unusual, and surprising and potentially scary in aspects of class-loading and testing with rails scripts. So I've decided to ask

andriytyurnikov commented 6 years ago

@andrzejkrzywda , I guess, option of creating module manually within /lib is totally possible for me without generator :D But perhaps current behavior is not what you guys wanted ;)

andrzejkrzywda commented 6 years ago

@andriytyurnikov Yeah, it's all super simple here and I'd say it's still a subject for changes. We're usually OK with having bounded contexts as directories in the main Rails directory, but I agree it's not usual in the Rails world. I'm happy that you tried it and we can have this discussion which place is best, thanks!

mostlyobvious commented 6 years ago

@andriytyurnikov

For rspec flavour we actually do the following:

       # spec/identity_access_spec.rb

       require 'rails_helper'

       path = Rails.root.join('identity_access/spec')
       Dir.glob("#{path}/**/*_spec.rb") do |file|
         require file
       end

which would require spec files from identity_access/spec and you won't miss it in standard rails + rspec test run.

For test-unit that doesn't happen yet and I agree that might be confusing. It was initially extracted from spec-backed project.

Here are corresponding test cases for the reference: https://github.com/RailsEventStore/rails_event_store/blob/master/bounded_context/spec/generators_spec.rb#L62-L73

andriytyurnikov commented 6 years ago

@andrzejkrzywda , good that you've created a documentation task. I'll try to contribute, and possibly illustrate potential issues with class reloading from root path. However, rationale behing the very concept of BC is up to you guys, I am just starting with EventSourcing

@pawelpacana, well, I am minitest guy, I'll check out spec and test generators right away.

Closing in favor of #187

andrzejkrzywda commented 6 years ago

minitest ❤️

mostlyobvious commented 6 years ago

I believe this can be useful for docs replacement by now: https://blog.arkency.com/rails-components-neither-engines-nor-gems/

andriytyurnikov commented 6 years ago

@pawelpacana , @andrzejkrzywda I wonder if you guys are open to doubts about current choice?)

paneq commented 6 years ago

Which is fairly unusual

indeed and that's the point for me.

In my opinion the fact that you have a top-level directory is the best mental barrier to actually remember that it is a separate context. Put it in app/ or lib/ does not achieve the same effect, I believe.

However, no professional research has been made. Just a personal gut feeling.

and surprising

maybe not... https://www.youtube.com/watch?v=WpkDN78P884

potentially scary in aspects of class-loading

not really https://blog.arkency.com/2014/11/dont-forget-about-eager-load-when-extending-autoload/ - this part is most simple imho

and testing with rails scripts

This part might require most work to integrate properly.

andriytyurnikov commented 6 years ago

@paneq , well, big question is: are BCs that independent? Bcoz if they need access to some defined environment (EventStore etc.) which is not injected/abstracted or something, then they are not :)

paneq commented 6 years ago

I distinguish between infrastructure isolation (they can share EventStore, DBs, app server, gems) and conceptual, business isolation.

andriytyurnikov commented 6 years ago

@paneq , you were right about autoloading of models. I'll go with plain old /lib modules though, untill docs are present

paneq commented 6 years ago

👍

andrzejkrzywda commented 6 years ago

I wonder if you guys are open to doubts about current choice?)

Of course! I'm not certain our current suggestion is the best one. Happy to hear other opinions.

paneq commented 6 years ago

@andriytyurnikov Also, we would be happy if you fill out our poll about RES usage: https://goo.gl/forms/ecNpJsL5qM2M8Ls93 :) Thank you and have a great day!

andriytyurnikov commented 6 years ago

As explaining something to beginner may be useful exercise for documentation writer, I would like to ask @paneq and @pawelpacana for criticism of my understanding of the topic of BoundedContext.

Following code (may be using existing constants) kinda reflects an idea of Bounded Context

module AccountManagementContext

  module Commands
    Correct
      # Queries::AccountByUUID
      # Events::Corrected      
    SignUp                    
      # Queries::EmailAvialable 
      # Events::SignedUp
    SignIn                    
      # Queries::AccountByEmail 
      # Queries::IsValidEncryptedPasswordForAccount 
      # Events::SignedIn
    SignOut                   
      # Queries::AccountByEmail 
      # Events::AccountSignedOut
    PasswordResetTokenRequest 
      # Queries::AccountByEmail
      # Events::PasswordResetLinkRequested
    PasswordResetTokenSend    
      # Queries::AccountByEmail 
      # Events::PasswordResetLinkSent
    PasswordReset
      # Queries::AccountByPasswordResetToken 
      # Queries::IsValidPasswordResetTokenForAccount
      # Events::AccountPasswordUpdated
  end

  module Queries
    IsEmailAvialable
    AccountByUUID
    AccountByEmail
    AccountByPasswordResetToken
    IsValidEncryptedPasswordForAccount    
    IsValidAuthTokenForAccount            
    IsValidPasswordResetTokenForAccount   
  end

  module Aggregates # collections of events
    Account            # (AccountSignedUp, AccountSignedIn, AccountSignedOut)
    Activity            #(*)
    AuthToken          # (AccountSignedUp, AccountSignedIn, AccountSignedOut)
    PasswordResetToken # (PasswordResetLinkRequested, PasswordResetLinkSent, AccountPasswordUpdated)
  end

  module Events
    Corrected                   # (account_id, email, ecrypted_password, auth_token, auth_token_expiration, password_reset_token, password_reset_token_expiration)
    SignedUp                    # (account_id, email, ecrypted_password, auth_token, auth_token_expiration)
    SignedIn                    # (account_id, auth_token, auth_token_expiration)
    SignedOut                   # (account_id, auth_token)
    PasswordResetLinkRequested  # (account_id)
    PasswordResetLinkSent       # (account_id, email, password_reset_token, password_reset_token_expiration)
    PasswordUpdated             # (account_id, password_reset_token, ecrypted_password)
  end

end

Here AccountManagementContext is an isolated module of application. Module that governs it's state, as state only may be changed through commands, which use Queries and persist Events (as representation of facts).

While naming convention gives rough understanding of context at hand, and may be used to enforce deeper isolation, as framework may be written in a way that enforces particular constant lookup order or in other ways prohibits creation of Events from outside of Commands module of corresponding Bounded Context (AccountManagementContext).