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



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

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



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

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



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

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



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

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



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

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



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

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



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

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



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

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