npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

sf-archguard

v0.3.0

Published

ArchUnit-style architecture enforcement for Salesforce SFDX projects

Readme

SF-ArchGuard: Architecture Enforcement for Salesforce SFDX Projects

The Problem: Uncontrolled Complexity in Salesforce Development

As Salesforce organizations grow, so does the complexity of their codebase. Multiple teams, diverse functionalities, and continuous development often lead to:

  • Dependency Drift: Modules and packages unintentionally start relying on each other, breaking intended architectural boundaries.
  • Slow, Risky Deployments: Tightly coupled components force larger deployments, increasing risk and slowing down release cycles.
  • Eroding Maintainability: A lack of clear architectural guardrails makes it harder to understand, test, and evolve the application.

This results in a cycle of technical debt, hindering agility and team productivity.

The Solution: Enforce Architectural Rules with SF-ArchGuard

SF-ArchGuard provides architecture enforcement for Salesforce SFDX projects, similar to how ArchUnit works for Java. It helps you define clear architectural layers and package boundaries, then automatically catches illegal cross-package and cross-layer dependencies in your Apex classes, triggers, and custom object relationships.

Key Benefits:

  • Preserve Architectural Integrity: Automatically detect and prevent unintended dependencies.
  • Enable Faster, Safer Deployments: Maintain smaller, more independent deployment units.
  • Boost Developer Productivity: Clearly defined boundaries reduce confusion and support multi-stream development.
  • Automate Compliance: Integrate into your CI/CD pipeline to enforce rules continuously.

Quickstart: Get Started in 3 Steps

SF-ArchGuard is designed for seamless integration with your Salesforce DX workflow.

  1. Install the Salesforce CLI Plugin:

    sf plugins install sf-archguard
  2. Initialize Your Project Configuration: Generate a starter archguard.yml configuration file in your project root:

    sf archguard init

    This command intelligently scans your project's sfdx-project.json and existing metadata to suggest initial layers and packages.

  3. Enforce Your Architecture: Run SF-ArchGuard to check for violations against your defined rules:

    sf archguard enforce

    Violations will be reported directly in your console. For CI/CD integration, you can generate JSON or JUnit reports (see CI Integration).

Example archguard.yml (Generated by sf archguard init):

layers:
  - name: integration
    dependsOn: [service, shared]
  - name: service
    dependsOn: [shared]
  - name: shared
    dependsOn: []

packages:
  billing:
    path: force-app/main/default/billing
    layer: service
  payments:
    path: force-app/main/default/payments
    layer: integration
    dependsOn: [billing] # explicit cross-package exception
  common:
    path: force-app/main/default/common
    layer: shared

rules:
  enforcePackageBoundaries: true
  enforceObjectBoundaries: true
  exclude:
    - "**/*Test.cls"
    - "**/*Mock.cls"

What SF-ArchGuard Checks

SF-ArchGuard focuses exclusively on structural and architectural rules. It does not duplicate PMD, ESLint, or other linting tools — there are no code style, complexity, or security checks here.

  • Layer dependency direction — e.g., a service layer package cannot depend on an integration layer package if not explicitly allowed.
  • Package boundaries — Apex classes can only reference classes from the same package, an allowed layer, or an explicitly declared dependency.
  • Object boundaries — Custom object lookups and master-detail relationships respect the same package/layer rules.

Violation Output

Every violation explains what broke and why it's disallowed. Run with --verbose to also get a concrete fix suggestion and the list of allowed targets.

Default output — one-line "why" under each violation:

✗ layer-dependency: 1 error (8 edges checked)
  [ERROR] force-app/main/default/billing/objects/Invoice__c/Invoice__c.object-meta.xml
    Invoice__c → Payment__c (billing → payments)
    Layer "service" may only depend on: shared, or "service" itself (lateral).

Verbose output (--verbose) — adds Fix: and Allowed targets::

✗ layer-dependency: 1 error (8 edges checked)
  [ERROR] force-app/main/default/billing/objects/Invoice__c/Invoice__c.object-meta.xml
    Layer violation: "Invoice__c" (billing, layer: service) depends on "Payment__c" (payments, layer: integration). Layer "service" is not allowed to depend on layer "integration".
    billing/Invoice__c → payments/Payment__c
    Why:  Layer "service" may only depend on: shared, or "service" itself (lateral).
    Fix:  Either move "Payment__c" into an allowed layer, or add "integration" to layers[service].dependsOn in archguard.yml.
    Allowed targets: shared

JSON and JUnit reports always include explanation, suggestion, and allowedAlternatives fields on every violation, so CI pipelines surface the full context without needing a flag.

When To Use It

SF-ArchGuard is most useful when a Salesforce codebase is split into multiple logical modules that should be developed and deployed separately, while still living in the same repository and SFDX project.

Typical cases:

  • Large orgs with distinct domains such as billing, payments, CRM, or notifications.
  • Teams trying to keep deployments fast by shipping only the affected module.
  • Projects getting close to Salesforce deployment scale limits, including the 10,000-component limit for a single deployment.
  • Repositories where architectural boundaries are defined up front, but tend to erode over time during normal feature work.

Without automated boundary checks, one module can quietly start depending on another in ways that make separate deployment harder. That usually shows up later as slow deployments, unexpected metadata coupling, or an inability to deploy a large package cleanly because too many components now need to move together.

Example Project Structure

One common setup is a single repo with subfolders representing modules that should remain independently deployable:

force-app/
  main/
    default/
      billing/
      payments/
      crm/
      notifications/
      core-domain/
      common/

Example intent for those folders:

  • billing contains invoice and receivables logic
  • payments contains payment gateway integrations
  • crm contains customer service workflows
  • notifications contains email, SMS, or platform event delivery
  • core-domain contains reusable business entities and domain logic
  • common contains shared technical utilities

In that structure, payments may be allowed to depend on billing, and both may use common, but common should not depend on payments. A CRM module also should not reach into billing internals just because both folders sit under the same SFDX source tree.

SF-ArchGuard makes those boundaries executable during development, before dependency drift turns into deployment coupling.

When the codebase is split well and those boundaries are actively governed, teams usually get practical delivery benefits as well:

  • Easier packaging and clearer ownership of what belongs in each package.
  • Easier multi-stream development, because teams can work in parallel with less accidental overlap.
  • More scratch-org-friendly development, since smaller and cleaner module scopes are easier to push and validate.
  • More deployment-friendly release flows, because the deployable unit stays smaller and more predictable.

How Dependency Rules Work

Dependencies are allowed based on two mechanisms working together:

Layer rules define the general direction. A layer can only depend on layers listed in its dependsOn. Same-layer references are always allowed (lateral dependencies).

integration  -->  service  -->  domain  -->  shared
     \                \            
      `-> shared       `-> shared   `-> (nothing else)

Package-level dependsOn adds specific exceptions. If payments declares dependsOn: [billing], then classes in the payments package can reference billing classes regardless of layer rules.

Same-package references are always allowed — classes within a single package can freely reference each other.

This is especially useful when packages map to deployment units. Keeping dependencies limited to approved layers and explicitly declared package links helps preserve smaller deployment scopes and reduces the risk that unrelated modules must be deployed together.

What Gets Analyzed

Apex classes and triggers — the parser detects:

  • extends / implements (inheritance)
  • Type usage in declarations (BillingService svc = ...)
  • Generic type parameters (List<InvoiceWrapper>)
  • Static method calls (BillingService.createInvoice())
  • new ClassName() instantiation
  • instanceof checks and cast expressions
  • SOQL FROM Object__c references
  • Trigger on SObject declarations

Custom objects and fields — the parser reads .object-meta.xml and .field-meta.xml to detect:

  • Lookup relationships (referenceTo)
  • Master-Detail relationships (referenceTo)
  • Formula field object references (Object__c.Field__c patterns)

Command Reference

sf archguard enforce

| Flag | Short | Description | Default | |---|---|---|---| | --project-dir <path> | -p | SFDX project root directory | Current directory | | --config <path> | -c | Path to archguard.yml | Auto-detected in project root | | --format <format> | -f | Output: console, json, or junit | console | | --output <path> | -o | Write report to file (json/junit) | stdout | | --verbose | -v | Add Fix: suggestion and Allowed targets: list to each violation | false | | --[no-]fail-on-violation | | Exit code 1 on violations | true |

Other Installation Options

As a standalone global CLI:

npm install -g sf-archguard
sf-archguard --help

As a library (for programmatic use — see Programmatic API):

npm install sf-archguard

CI Integration

Use the JUnit output format to surface violations in any CI pipeline:

GitHub Actions:

- name: Enforce architecture
  run: sf archguard enforce --format junit --output archguard-report.xml

- name: Publish test results
  uses: mikepenz/action-junit-report@v4
  if: always()
  with:
    report_paths: archguard-report.xml
    check_name: Architecture Violations

Fail the build on violations (default --fail-on-violation is true; disable with --no-fail-on-violation for report-only mode).

Coming Soon: A dedicated public GitHub Action is in development. It will provide a turnkey setup without needing to write bash scripts, and will natively annotate your pull requests with architectural violations inline!

What This Tool Does NOT Do

SF-ArchGuard is intentionally narrow in scope. The following are handled by other tools and should not be duplicated here:

  • Code style and formatting (use Prettier, PMD)
  • Cyclomatic complexity and method length (use PMD)
  • Security scanning (use PMD security rules, Checkmarx)
  • SOQL injection detection (use PMD)
  • Naming conventions (use PMD)
  • Governor limit analysis (use Salesforce Scanner)

Community and Contributing

We welcome contributions from the community! Whether it's reporting a bug, proposing a feature, or submitting a pull request, your input helps make sf-archguard better.

  • Report Bugs & Request Features: Please use our GitHub Issue Tracker. We have templates for bug reports and feature requests to make it easier for you to provide the necessary information.
  • Contribute Code: Read our CONTRIBUTING.md guide for details on our development process and the steps for submitting pull requests.
  • Code of Conduct: Please review and adhere to our Code of Conduct to ensure a welcoming and inclusive environment for everyone.

License

MIT