Module: ArchSpec::Architectures
Constant Summary collapse
- DEFAULT_LAYERED =
{ interface: 'app/controllers/**/*.rb', application: %w[app/services/**/*.rb app/jobs/**/*.rb app/mailers/**/*.rb], domain: 'app/models/**/*.rb' }.freeze
- DEFAULT_RAILS_MVC =
{ controllers: 'app/controllers/**/*.rb', models: 'app/models/**/*.rb', helpers: 'app/helpers/**/*.rb', mailers: 'app/mailers/**/*.rb', jobs: 'app/jobs/**/*.rb', services: 'app/services/**/*.rb' }.freeze
- DEFAULT_HEXAGONAL =
{ application: %w[app/services/**/*.rb app/use_cases/**/*.rb], domain: 'app/domain/**/*.rb', ports: 'app/ports/**/*.rb', adapters: %w[app/adapters/**/*.rb app/integrations/**/*.rb app/infrastructure/**/*.rb] }.freeze
- DEFAULT_CLEAN =
{ frameworks: %w[app/controllers/**/*.rb app/jobs/**/*.rb app/mailers/**/*.rb], interface_adapters: %w[app/adapters/**/*.rb app/presenters/**/*.rb app/serializers/**/*.rb], use_cases: %w[app/use_cases/**/*.rb app/services/**/*.rb], entities: %w[app/entities/**/*.rb app/domain/**/*.rb app/models/**/*.rb] }.freeze
- DEFAULT_CQRS =
{ commands: 'app/commands/**/*.rb', queries: 'app/queries/**/*.rb', read_models: 'app/read_models/**/*.rb' }.freeze
- DEFAULT_EVENT_DRIVEN =
{ events: 'app/events/**/*.rb', publishers: 'app/publishers/**/*.rb', subscribers: 'app/subscribers/**/*.rb' }.freeze
- VANILLA_RAILS_EMPTY =
{ services: ['app/services/**/*.rb', 'behavior belongs on models, not service objects'], forms: ['app/forms/**/*.rb', 'use strong parameters and model validations'], policies: ['app/policies/**/*.rb', 'authorization is predicate methods on models'], decorators: ['app/decorators/**/*.rb', 'use helpers and ERB partials'], presenters: ['app/presenters/**/*.rb', 'presentation objects are POROs in app/models'], view_components: ['app/components/**/*.rb', 'use helpers and ERB partials'] }.freeze
- CONTROLLER_METHODS =
%i[render redirect_to params session cookies flash].freeze
- MUTATING_METHODS =
%i[ create create! delete delete_all destroy destroy! insert insert! save save! update update! update_attribute update_attributes update_columns upsert upsert! ].freeze
Instance Method Summary collapse
- #apply(name, dsl, **options) ⇒ Object
- #clean(dsl, frameworks:, interface_adapters:, use_cases:, entities:) ⇒ Object
- #cqrs(dsl, commands:, queries:, read_models: nil, mutating_methods: MUTATING_METHODS) ⇒ Object
- #event_driven(dsl, events:, publishers:, subscribers:) ⇒ Object
- #hexagonal(dsl, application:, domain:, ports:, adapters:) ⇒ Object
- #layered(dsl, layers:) ⇒ Object
- #modular_monolith(dsl, components:, allow: {}) ⇒ Object
- #rails_mvc(dsl, components:) ⇒ Object
- #rails_strict(dsl, components:) ⇒ Object
- #vanilla_rails(dsl, components:, empty:) ⇒ Object
Instance Method Details
#apply(name, dsl, **options) ⇒ Object
68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 |
# File 'lib/archspec/architectures.rb', line 68 def apply(name, dsl, **) case name.to_sym when :rails, :rails_mvc, :rails_way rails_mvc(dsl, components: .fetch(:components, DEFAULT_RAILS_MVC)) when :rails_strict rails_strict(dsl, components: .fetch(:components, DEFAULT_RAILS_MVC)) when :vanilla_rails vanilla_rails( dsl, components: .fetch(:components, DEFAULT_RAILS_MVC), empty: .fetch(:empty, VANILLA_RAILS_EMPTY) ) when :layered, :rails_layered layered(dsl, layers: .fetch(:layers, DEFAULT_LAYERED)) when :hexagonal, :rails_hexagonal hexagonal(dsl, **with_defaults(DEFAULT_HEXAGONAL, )) when :clean, :rails_clean clean(dsl, **with_defaults(DEFAULT_CLEAN, )) when :modular_monolith, :bounded_contexts modular_monolith(dsl, components: .fetch(:components), allow: .fetch(:allow, {})) when :cqrs, :rails_cqrs cqrs(dsl, **with_defaults(DEFAULT_CQRS, )) when :event_driven, :rails_event_driven event_driven(dsl, **with_defaults(DEFAULT_EVENT_DRIVEN, )) else raise Error, "Unknown ArchSpec architecture: #{name.inspect}" end end |
#clean(dsl, frameworks:, interface_adapters:, use_cases:, entities:) ⇒ Object
152 153 154 155 156 157 158 159 160 161 162 |
# File 'lib/archspec/architectures.rb', line 152 def clean(dsl, frameworks:, interface_adapters:, use_cases:, entities:) layered( dsl, layers: { frameworks: frameworks, interface_adapters: interface_adapters, use_cases: use_cases, entities: entities } ) end |
#cqrs(dsl, commands:, queries:, read_models: nil, mutating_methods: MUTATING_METHODS) ⇒ Object
176 177 178 179 180 181 182 183 184 185 |
# File 'lib/archspec/architectures.rb', line 176 def cqrs(dsl, commands:, queries:, read_models: nil, mutating_methods: MUTATING_METHODS) components = normalize_map(commands: commands, queries: queries) components[:read_models] = read_models if read_models define_components(dsl, components) proxy_for(dsl, :commands).cannot_use :queries proxy_for(dsl, :queries).cannot_use :commands proxy_for(dsl, :queries).cannot_call(*mutating_methods) dsl.no_cycles!(among: components.keys) end |
#event_driven(dsl, events:, publishers:, subscribers:) ⇒ Object
187 188 189 190 191 192 193 194 195 |
# File 'lib/archspec/architectures.rb', line 187 def event_driven(dsl, events:, publishers:, subscribers:) roles = normalize_map(events: events, publishers: publishers, subscribers: subscribers) define_components(dsl, roles) proxy_for(dsl, :events).cannot_use :publishers, :subscribers proxy_for(dsl, :publishers).can_use :events proxy_for(dsl, :subscribers).can_use :events dsl.no_cycles!(among: roles.keys) end |
#hexagonal(dsl, application:, domain:, ports:, adapters:) ⇒ Object
136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 |
# File 'lib/archspec/architectures.rb', line 136 def hexagonal(dsl, application:, domain:, ports:, adapters:) roles = normalize_map( application: application, domain: domain, ports: ports, adapters: adapters ) define_components(dsl, roles) proxy_for(dsl, :application).can_use :domain, :ports proxy_for(dsl, :domain).cannot_use :adapters proxy_for(dsl, :ports).cannot_use :adapters proxy_for(dsl, :adapters).can_use :application, :domain, :ports dsl.no_cycles!(among: roles.keys) end |
#layered(dsl, layers:) ⇒ Object
123 124 125 126 127 128 129 130 131 132 133 134 |
# File 'lib/archspec/architectures.rb', line 123 def layered(dsl, layers:) ordered = normalize_map(layers) define_components(dsl, ordered) names = ordered.keys names.each_with_index do |name, index| allowed = names[(index + 1)..] || [] proxy_for(dsl, name).can_use(*allowed) end dsl.no_cycles!(among: names) end |
#modular_monolith(dsl, components:, allow: {}) ⇒ Object
164 165 166 167 168 169 170 171 172 173 174 |
# File 'lib/archspec/architectures.rb', line 164 def modular_monolith(dsl, components:, allow: {}) components = normalize_map(components) define_components(dsl, components) components.each_key do |name| allowed = Array(allow[name] || allow[name.to_s]) proxy_for(dsl, name).can_use(*allowed) end dsl.no_cycles!(among: components.keys) end |
#rails_mvc(dsl, components:) ⇒ Object
97 98 99 100 101 102 103 104 105 106 |
# File 'lib/archspec/architectures.rb', line 97 def rails_mvc(dsl, components:) components = normalize_map(components) define_components(dsl, components) proxy_for(dsl, :controllers).can_use(*components.keys & %i[models services helpers mailers jobs]) proxy_for(dsl, :models).cannot_use(*components.keys & %i[controllers helpers]) proxy_for(dsl, :services).cannot_use(*components.keys & %i[controllers helpers]) proxy_for(dsl, :models).cannot_call(*CONTROLLER_METHODS) proxy_for(dsl, :services).cannot_call(*CONTROLLER_METHODS) end |
#rails_strict(dsl, components:) ⇒ Object
108 109 110 111 112 113 |
# File 'lib/archspec/architectures.rb', line 108 def rails_strict(dsl, components:) components = normalize_map(components) rails_mvc(dsl, components: components) dsl.verify_zeitwerk_names! dsl.no_cycles!(among: components.keys) end |
#vanilla_rails(dsl, components:, empty:) ⇒ Object
115 116 117 118 119 120 121 |
# File 'lib/archspec/architectures.rb', line 115 def vanilla_rails(dsl, components:, empty:) rails_mvc(dsl, components: components) empty.each do |name, (pattern, reason)| dsl.component(name, in: pattern).must_be_empty(because: reason) end end |