Module: ArchSpec::Architectures

Extended by:
Architectures
Included in:
Architectures
Defined in:
lib/archspec/architectures.rb

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

Instance Method Details

#apply(name, dsl, **options) ⇒ Object



59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
# File 'lib/archspec/architectures.rb', line 59

def apply(name, dsl, **options)
  case name.to_sym
  when :rails_mvc, :rails_way
    rails_mvc(dsl, components: options.fetch(:components, DEFAULT_RAILS_MVC))
  when :layered
    layered(dsl, layers: options.fetch(:layers, DEFAULT_LAYERED))
  when :hexagonal
    hexagonal(dsl, **with_defaults(DEFAULT_HEXAGONAL, options))
  when :clean
    clean(dsl, **with_defaults(DEFAULT_CLEAN, options))
  when :modular_monolith, :bounded_contexts
    modular_monolith(dsl, components: options.fetch(:components), allow: options.fetch(:allow, {}))
  when :cqrs
    cqrs(dsl, **with_defaults(DEFAULT_CQRS, options))
  when :event_driven
    event_driven(dsl, **with_defaults(DEFAULT_EVENT_DRIVEN, options))
  else
    raise Error, "Unknown ArchSpec architecture: #{name.inspect}"
  end
end

#clean(dsl, frameworks:, interface_adapters:, use_cases:, entities:) ⇒ Object



120
121
122
123
124
125
126
127
128
129
130
# File 'lib/archspec/architectures.rb', line 120

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



144
145
146
147
148
149
150
151
152
153
# File 'lib/archspec/architectures.rb', line 144

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



155
156
157
158
159
160
161
162
163
# File 'lib/archspec/architectures.rb', line 155

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



104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
# File 'lib/archspec/architectures.rb', line 104

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



91
92
93
94
95
96
97
98
99
100
101
102
# File 'lib/archspec/architectures.rb', line 91

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



132
133
134
135
136
137
138
139
140
141
142
# File 'lib/archspec/architectures.rb', line 132

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



80
81
82
83
84
85
86
87
88
89
# File 'lib/archspec/architectures.rb', line 80

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