Files
2nd/10_Wiki/Topics/Architecture/Architectural-Constraint-Enforcement.md
T
koriweb d8a80f6272 chore(wiki): dangling 링크 canonical 정규화 (768파일/1200건)
이름만 다른(표기 변형) [[위키링크]]를 대상 문서의 canonical 제목으로 치환해
끊겼던 1,200개 링크를 연결. 제목/파일명 정규화 일치만 적용하고 별칭 매칭은
과병합 위험으로 제외(애매성 가드). 원본은 _link_reconcile_backup/ 에 백업.
도구: Datacollect/scripts/link_reconcile_apply.mjs

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-08 12:24:15 +09:00

6.3 KiB

id, title, category, status, canonical_id, aliases, duplicate_of, source_trust_level, confidence_score, verification_status, tags, raw_sources, last_reinforced, github_commit, tech_stack
id title category status canonical_id aliases duplicate_of source_trust_level confidence_score verification_status tags raw_sources last_reinforced github_commit tech_stack
wiki-2026-0508-architectural-constraint-enforce Architectural Constraint Enforcement 10_Wiki/Topics verified self
archunit
dependency-cruiser
fitness-functions
none A 0.9 applied
architecture
fitness-functions
archunit
constraints
2026-05-10 pending
language framework
java archunit

Architectural Constraint Enforcement

매 한 줄

"매 architecture 의 자동으로 enforce 한다". 매 ArchUnit (2018, Java/Kotlin), Dependency-Cruiser (JS/TS), NetArchTest (.NET), Konsist (Kotlin) 의 fitness function 의 CI 통합. 매 Building Evolutionary Architectures (Ford, Parsons, Kua, 2017; 2nd ed 2023) 의 paradigm.

매 핵심

매 enforced rules (typical)

  • Layer dependency: 매 controller → service → repository — 매 reverse 의 X.
  • Package isolation: domain 의 framework import 의 X.
  • Naming: 매 *Service class 의 service package 의 only.
  • Cyclic dependency: 매 always X.
  • API surface: 매 internal/* 의 external module 의 import 의 X.

매 fitness function categories (Ford)

  • Atomic vs Holistic: single attribute / system-wide.
  • Triggered vs Continual: CI gate / runtime probe.
  • Static vs Dynamic: code analysis / runtime metric.
  • Automated vs Manual: prefer automated.

매 응용

  1. CI gate — 매 PR 의 violation 의 block.
  2. Refactor safety — 매 large refactor 의 invariant 의 hold.
  3. Onboarding — 매 implicit rule 의 explicit code 의 됨.

💻 패턴

ArchUnit — layer enforcement (Java)

import static com.tngtech.archunit.lang.syntax.ArchRuleDefinition.*;
import com.tngtech.archunit.junit.AnalyzeClasses;
import com.tngtech.archunit.junit.ArchTest;
import com.tngtech.archunit.lang.ArchRule;

@AnalyzeClasses(packages = "com.acme.banking")
class LayerArchTest {
  @ArchTest
  static final ArchRule controllers_only_call_services =
    classes().that().resideInAPackage("..controller..")
      .should().onlyDependOnClassesThat()
      .resideInAnyPackage("..controller..", "..service..", "java..", "org.springframework..");

  @ArchTest
  static final ArchRule no_cycles =
    slices().matching("..banking.(*)..").should().beFreeOfCycles();

  @ArchTest
  static final ArchRule services_named_correctly =
    classes().that().resideInAPackage("..service..")
      .and().areNotInterfaces()
      .should().haveSimpleNameEndingWith("Service");
}

Dependency-Cruiser (TypeScript)

// .dependency-cruiser.cjs
module.exports = {
  forbidden: [
    {
      name: 'no-circular',
      severity: 'error',
      from: {},
      to: { circular: true }
    },
    {
      name: 'domain-pure',
      severity: 'error',
      from: { path: '^src/domain' },
      to: { path: '^(src/infra|node_modules/express)' }
    },
    {
      name: 'no-test-in-prod',
      severity: 'error',
      from: { pathNot: '\\.test\\.ts$' },
      to: { path: '\\.test\\.ts$' }
    }
  ],
  options: { tsConfig: { fileName: 'tsconfig.json' } }
};
npx depcruise src --config .dependency-cruiser.cjs
npx depcruise src --output-type dot | dot -T svg > deps.svg

NetArchTest (.NET 8)

using NetArchTest.Rules;
using Xunit;

public class ArchitectureTests {
  [Fact]
  public void Domain_ShouldNotDependOnInfrastructure() {
    var result = Types.InAssembly(typeof(Domain.Marker).Assembly)
      .That().ResideInNamespace("Acme.Domain")
      .ShouldNot().HaveDependencyOn("Acme.Infrastructure")
      .GetResult();
    Assert.True(result.IsSuccessful, string.Join(",", result.FailingTypeNames ?? []));
  }
}

Konsist (Kotlin)

import com.lemonappdev.konsist.api.Konsist
import com.lemonappdev.konsist.api.verify.assertTrue

class CleanArchitectureTest {
  @Test
  fun `domain layer does not depend on data layer`() {
    Konsist.scopeFromProduction()
      .files
      .filter { it.packagee?.fullyQualifiedName?.contains("domain") == true }
      .assertTrue { file ->
        file.imports.none { it.name.contains(".data.") }
      }
  }
}

Go — go-arch-lint

# .go-arch-lint.yml
version: 3
workdir: internal
components:
  domain: { in: domain/** }
  app:    { in: app/** }
  infra:  { in: infra/** }
deps:
  domain: {}
  app:    { mayDependOn: [domain] }
  infra:  { mayDependOn: [domain, app] }

Python — import-linter

# .importlinter
[importlinter]
root_package = acme

[importlinter:contract:layers]
name = Layered architecture
type = layers
layers =
    acme.api
    acme.service
    acme.domain

CI integration (GitHub Actions)

- name: Architecture tests
  run: |
    ./gradlew archTest
    npx depcruise src --config .dependency-cruiser.cjs
    lint-imports

매 결정 기준

Stack Tool
Java/Kotlin ArchUnit, Konsist
JS/TS Dependency-Cruiser, eslint-plugin-boundaries
.NET NetArchTest
Go go-arch-lint
Python import-linter
Polyglot Sonargraph, Structure101 (commercial)

기본값: 매 ArchUnit (JVM) / Dependency-Cruiser (Node) — 매 OSS + CI-first.

🔗 Graph

🤖 LLM 활용

언제: 매 plain English rule → ArchUnit/depcruise config 의 translation, 매 violation message 의 fix suggestion. 언제 X: 매 architecture 의 design 의 LLM-only delegation — 매 human ownership 의 필수.

안티패턴

  • Test exists, never runs: 매 CI 의 not wired — 매 dead rule.
  • Over-broad rule: 매 100% violations 의 noise — 매 graduated rollback (allowlist).
  • Rule without rationale: 매 ADR-less rule — 매 future deletion 의 blocker.
  • Ignore-list explosion: 매 exception 의 100+ — 매 architecture 의 already eroded 의 sign.

🧪 검증 / 중복

  • Verified (Ford et al., Building Evolutionary Architectures 2nd ed; ArchUnit user guide).
  • 신뢰도 A.

🕓 Changelog

날짜 변경
2026-05-08 Phase 1
2026-05-10 Manual cleanup — fitness functions + ArchUnit/depcruise/NetArchTest patterns