Skip to content

Commit

Permalink
replacing architecture with layer
Browse files Browse the repository at this point in the history
  • Loading branch information
perryqh committed Apr 26, 2024
1 parent 1ad792d commit b23fe28
Show file tree
Hide file tree
Showing 10 changed files with 72 additions and 72 deletions.
2 changes: 1 addition & 1 deletion Gemfile.lock
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ GIT
PATH
remote: .
specs:
packwerk-extensions (0.1.11)
packwerk-extensions (0.2.0)
packwerk (>= 2.2.1)
railties (>= 6.0.0)
sorbet-runtime
Expand Down
14 changes: 7 additions & 7 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ Currently, it ships the following checkers to help improve the boundaries betwee
- A `privacy` checker that ensures other packages are using your package's public API
- A `visibility` checker that allows packages to be private except to an explicit group of other packages.
- A `folder_visibility` checker that allows packages to their sibling packs and parent pack (to be used in an application that uses folder packs)
- An `architecture` checker that allows packages to specify their "layer" and requires that each layer only communicate with layers below it.
- An `layer` checker that allows packages to specify their "layer" and requires that each layer only communicate with layers below it.

## Installation

Expand All @@ -26,7 +26,7 @@ require:
- packwerk/privacy/checker
- packwerk/visibility/checker
- packwerk/folder_visibility/checker
- packwerk/architecture/checker
- packwerk/layer/checker
```
## Privacy Checker
Expand Down Expand Up @@ -194,20 +194,20 @@ packs/b/packs/h OK (sibling)
packs/c VIOLATION
```
## Architecture Checker
The architecture checker can be used to enforce constraints on what can depend on what.
## Layer Checker
The layer checker can be used to enforce constraints on what can depend on what.
To enforce architecture for your package, first define the `architecture_layers` in `packwerk.yml`, for example:
To enforce layers for your package, first define the `layers` in `packwerk.yml`, for example:
```
architecture_layers:
layers:
- package
- utility
```
Then, turn on the checker in your package:
```yaml
# components/merchandising/package.yml
enforce_architecture: true
enforce_layers: true
layer: utility
```

Expand Down
2 changes: 1 addition & 1 deletion lib/packwerk-extensions.rb
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
require 'packwerk/privacy/checker'
require 'packwerk/visibility/checker'
require 'packwerk/folder_visibility/checker'
require 'packwerk/architecture/checker'
require 'packwerk/layer/checker'

module Packwerk
module Extensions
Expand Down
Original file line number Diff line number Diff line change
@@ -1,24 +1,24 @@
# typed: strict
# frozen_string_literal: true

require 'packwerk/architecture/layers'
require 'packwerk/architecture/package'
require 'packwerk/architecture/validator'
require 'packwerk/layer/layers'
require 'packwerk/layer/package'
require 'packwerk/layer/validator'

module Packwerk
module Architecture
module Layer
# This enforces "layered architecture," which allows each class to be designated as one of N layers
# configured by the client in `packwerk.yml`, for example:
#
# architecture_layers:
# layers:
# - orchestrator
# - business_domain
# - platform
# - utility
# - specification
#
# Then a package can configure:
# enforce_architecture: true | false | strict
# enforce_layers: true | false | strict
# layer: utility
#
# This is intended to provide:
Expand All @@ -30,7 +30,7 @@ class Checker
extend T::Sig
include Packwerk::Checker

VIOLATION_TYPE = T.let('architecture', String)
VIOLATION_TYPE = T.let('layer', String)

sig { override.returns(String) }
def violation_type
Expand All @@ -55,7 +55,7 @@ def invalid_reference?(reference)
end
def strict_mode_violation?(listed_offense)
constant_package = listed_offense.reference.package
constant_package.config['enforce_architecture'] == 'strict'
constant_package.config['enforce_layers'] == 'strict'
end

sig do
Expand All @@ -68,9 +68,9 @@ def message(reference)
referencing_package = Package.from(reference.package, layers)

message = <<~MESSAGE
Architecture layer violation: '#{reference.constant.name}' belongs to '#{reference.constant.package}', whose architecture layer type is "#{constant_package.layer}."
This constant cannot be referenced by '#{reference.package}', whose architecture layer type is "#{referencing_package.layer}."
Packs in a lower layer may not access packs in a higher layer. See the `architecture_layers` in packwerk.yml. Current hierarchy:
Layer violation: '#{reference.constant.name}' belongs to '#{reference.constant.package}', whose layer type is "#{constant_package.layer}."
This constant cannot be referenced by '#{reference.package}', whose layer type is "#{referencing_package.layer}."
Packs in a lower layer may not access packs in a higher layer. See the `layers` in packwerk.yml. Current hierarchy:
- #{layers.names_list.join("\n- ")}
#{standard_help_message(reference)}
Expand All @@ -92,7 +92,7 @@ def standard_help_message(reference)

sig { returns(Layers) }
def layers
@layers ||= T.let(Layers.new, T.nilable(Packwerk::Architecture::Layers))
@layers ||= T.let(Layers.new, T.nilable(Packwerk::Layer::Layers))
end
end
end
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
# frozen_string_literal: true

module Packwerk
module Architecture
module Layer
class Layers
extend T::Sig

Expand All @@ -29,7 +29,7 @@ def names

sig { returns(T::Array[String]) }
def names_list
@names_list ||= YAML.load_file('packwerk.yml')['architecture_layers'] || []
@names_list ||= YAML.load_file('packwerk.yml')['layers'] || []
end
end
end
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
# frozen_string_literal: true

module Packwerk
module Architecture
module Layer
class Package < T::Struct
extend T::Sig

Expand Down Expand Up @@ -46,7 +46,7 @@ def from(package, layers)

Package.new(
layer: layer,
enforcement_setting: config['enforce_architecture'],
enforcement_setting: config['enforce_layers'],
config: config
)
end
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
# frozen_string_literal: true

module Packwerk
module Architecture
module Layer
class Validator
extend T::Sig
include Packwerk::Validator
Expand All @@ -18,7 +18,7 @@ def call(package_set, configuration)
f = Pathname.new(package.name).join('package.yml').to_s
next if !config

result = check_enforce_architecture_setting(f, config['enforce_architecture'])
result = check_enforce_layers_setting(f, config['enforce_layers'])
results << result
next if !result.ok?

Expand All @@ -34,12 +34,12 @@ def call(package_set, configuration)

sig { returns(Layers) }
def layers
@layers ||= T.let(Layers.new, T.nilable(Packwerk::Architecture::Layers))
@layers ||= T.let(Layers.new, T.nilable(Packwerk::Layer::Layers))
end

sig { override.returns(T::Array[String]) }
def permitted_keys
%w[enforce_architecture layer]
%w[enforce_layers layer]
end

sig do
Expand All @@ -52,7 +52,7 @@ def check_layer_setting(package, config_file_path)
if layer.nil? && package.enforces?
Result.new(
ok: false,
error_value: "Invalid 'layer' option in #{config_file_path.inspect}: #{package.layer.inspect}. `layer` must be set if `enforce_architecture` is on."
error_value: "Invalid 'layer' option in #{config_file_path.inspect}: #{package.layer.inspect}. `layer` must be set if `enforce_layers` is on."
)
elsif valid_layer
Result.new(ok: true)
Expand All @@ -67,19 +67,19 @@ def check_layer_setting(package, config_file_path)
sig do
params(config_file_path: String, setting: T.untyped).returns(Result)
end
def check_enforce_architecture_setting(config_file_path, setting)
def check_enforce_layers_setting(config_file_path, setting)
activated_value = [true, 'strict'].include?(setting)
valid_value = [true, nil, false, 'strict'].include?(setting)
layers_set = layers.names.any?
if !valid_value
Result.new(
ok: false,
error_value: "Invalid 'enforce_architecture' option in #{config_file_path.inspect}: #{setting.inspect}"
error_value: "Invalid 'enforce_layers' option in #{config_file_path.inspect}: #{setting.inspect}"
)
elsif activated_value && !layers_set
Result.new(
ok: false,
error_value: "Cannot set 'enforce_architecture' option in #{config_file_path.inspect} until `architectural_layers` have been specified in `packwerk.yml`"
error_value: "Cannot set 'enforce_layers' option in #{config_file_path.inspect} until `layers` have been specified in `packwerk.yml`"
)
else
Result.new(ok: true)
Expand Down
2 changes: 1 addition & 1 deletion packwerk-extensions.gemspec
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
Gem::Specification.new do |spec|
spec.name = 'packwerk-extensions'
spec.version = '0.1.11'
spec.version = '0.2.0'
spec.authors = ['Gusto Engineers']
spec.email = ['[email protected]']

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,15 +4,15 @@
require 'test_helper'

module Packwerk
module Architecture
module Layer
class CheckerTest < Minitest::Test
extend T::Sig
include FactoryHelper
include RailsApplicationFixtureHelper

def write_config
write_app_file('packwerk.yml', <<~YML)
architecture_layers:
layers:
- orchestrator
- business_domain
- platform
Expand All @@ -22,11 +22,11 @@ def write_config
end

def orchestrator_pack(enforce: false)
Packwerk::Package.new(name: 'packs/orchestrator', config: { 'enforce_architecture' => enforce, 'layer' => 'orchestrator' })
Packwerk::Package.new(name: 'packs/orchestrator', config: { 'enforce_layers' => enforce, 'layer' => 'orchestrator' })
end

def utility_pack(enforce: false)
Packwerk::Package.new(name: 'packs/utility', config: { 'enforce_architecture' => enforce, 'layer' => 'utility' })
Packwerk::Package.new(name: 'packs/utility', config: { 'enforce_layers' => enforce, 'layer' => 'utility' })
end

setup do
Expand All @@ -40,7 +40,7 @@ def utility_pack(enforce: false)
end

test 'ignores if origin package is not enforcing' do
checker = architecture_checker
checker = layer_checker
reference = build_reference(
source_package: utility_pack(enforce: false),
destination_package: orchestrator_pack(enforce: false)
Expand All @@ -50,7 +50,7 @@ def utility_pack(enforce: false)
end

test 'is an invalid reference if destination pack is above source package' do
checker = architecture_checker
checker = layer_checker
reference = build_reference(
source_package: utility_pack(enforce: true),
destination_package: orchestrator_pack(enforce: false)
Expand All @@ -60,9 +60,9 @@ def utility_pack(enforce: false)
end

test 'infers layer based on root directory' do
orchestrator_pack = Packwerk::Package.new(name: 'orchestrator/some_pack', config: { 'enforce_architecture' => true })
utility_pack = Packwerk::Package.new(name: 'utility/some_other_pack', config: { 'enforce_architecture' => true })
checker = architecture_checker
orchestrator_pack = Packwerk::Package.new(name: 'orchestrator/some_pack', config: { 'enforce_layers' => true })
utility_pack = Packwerk::Package.new(name: 'utility/some_other_pack', config: { 'enforce_layers' => true })
checker = layer_checker
reference = build_reference(
source_package: utility_pack,
destination_package: orchestrator_pack
Expand All @@ -74,9 +74,9 @@ def utility_pack(enforce: false)
end

test 'allows layer setting to override root directory location' do
orchestrator_pack = Packwerk::Package.new(name: 'orchestrator/some_pack', config: { 'layer' => 'specification', 'enforce_architecture' => true })
utility_pack = Packwerk::Package.new(name: 'utility/some_other_pack', config: { 'enforce_architecture' => true })
checker = architecture_checker
orchestrator_pack = Packwerk::Package.new(name: 'orchestrator/some_pack', config: { 'layer' => 'specification', 'enforce_layers' => true })
utility_pack = Packwerk::Package.new(name: 'utility/some_other_pack', config: { 'enforce_layers' => true })
checker = layer_checker
reference = build_reference(
source_package: utility_pack,
destination_package: orchestrator_pack
Expand All @@ -88,7 +88,7 @@ def utility_pack(enforce: false)
end

test 'is not an invalid reference if destination pack is below source package' do
checker = architecture_checker
checker = layer_checker
reference = build_reference(
source_package: orchestrator_pack(enforce: true),
destination_package: utility_pack(enforce: false)
Expand All @@ -103,10 +103,10 @@ def utility_pack(enforce: false)
destination_package: orchestrator_pack(enforce: false)
)

assert_equal architecture_checker.message(reference), <<~MSG.chomp
Architecture layer violation: '::SomeName' belongs to 'packs/orchestrator', whose architecture layer type is "orchestrator."
This constant cannot be referenced by 'packs/utility', whose architecture layer type is "utility."
Packs in a lower layer may not access packs in a higher layer. See the `architecture_layers` in packwerk.yml. Current hierarchy:
assert_equal layer_checker.message(reference), <<~MSG.chomp
Layer violation: '::SomeName' belongs to 'packs/orchestrator', whose layer type is "orchestrator."
This constant cannot be referenced by 'packs/utility', whose layer type is "utility."
Packs in a lower layer may not access packs in a higher layer. See the `layers` in packwerk.yml. Current hierarchy:
- orchestrator
- business_domain
- platform
Expand All @@ -121,8 +121,8 @@ def utility_pack(enforce: false)
private

sig { returns(Checker) }
def architecture_checker
Packwerk::Architecture::Checker.new
def layer_checker
Packwerk::Layer::Checker.new
end
end
end
Expand Down
Loading

0 comments on commit b23fe28

Please sign in to comment.