Rulebearing Playground Coverage Schemas GitHub

Nothing imports the producer

The event producer is a process of its own: it drains the outbox and publishes to the broker. Code that imports it publishes from the request path instead, so a request now fails when the broker is down, and an event can be published for a change that is later rolled back. The answer is a rule that no module outside src/producer/ imports anything inside it.

The rule

from.pathNot leaves the producer's own modules out, so src/producer/main.ts may still start src/producer/producer.ts. Everything else, including modules added later, is held to it.

rulebearing.yaml
$schema: https://benbahrenburg.github.io/rulebearing/schema/config-v1.json
rules:
  dependencies:
    forbidden:
      - name: nothing-imports-the-producer
        comment: "The producer is its own process that drains the outbox; code that imports it publishes from the request path and fails when the broker is down. plan:0004-wave-4-reach"
        fix: "Write the event to the outbox (src/outbox/outbox.ts) and let the producer process publish it."
        severity: error
        from: { pathNot: "^src/producer/" }
        to: { path: "^src/producer/" }
        examples:
          forbidden:
            - "src/orders/cancel-order.ts -> src/producer/producer.ts"
          allowed:
            - "src/orders/place-order.ts -> src/outbox/outbox.ts"
            - "src/producer/main.ts -> src/producer/producer.ts"
            - "src/producer/producer.ts -> src/outbox/outbox.ts"

The file is rulebearing.yaml. Its examples are the edges rulebearing test checks the rule against.

The same rule for dependency-cruiser

rulebearing config convert rulebearing.yaml --to dependency-cruiser writes the rule in dependency-cruiser's format and names what it dropped: the $schema, the fix and the examples.

.dependency-cruiser.json
{
  "forbidden": [
    {
      "name": "nothing-imports-the-producer",
      "comment": "The producer is its own process that drains the outbox; code that imports it publishes from the request path and fails when the broker is down. plan:0004-wave-4-reach",
      "to": {
        "path": "^src/producer/"
      },
      "severity": "error",
      "from": {
        "pathNot": "^src/producer/"
      }
    }
  ]
}
dropped (stderr of config convert)
dropped 3 item(s) dependency-cruiser cannot hold:
  $schema: points at the native schema
  forbidden[nothing-imports-the-producer].fix: a Rulebearing rule field
  forbidden[nothing-imports-the-producer].examples: a Rulebearing rule field

The files are converted/.dependency-cruiser.json and converted/dropped.txt.

The failing edge

In repo/, src/orders/place-order.ts writes its event to the outbox, as it should. src/orders/cancel-order.ts writes to the outbox and then publishes straight away through the producer:

SeverityRuleFromToTypeThrough
errornothing-imports-the-producersrc/orders/cancel-order.tssrc/producer/producer.tsdependency

The fix

Write the event to the outbox and stop there; the producer process publishes it on its next pass:

// src/orders/cancel-order.ts
import { enqueue } from '../outbox/outbox';

export function cancelOrder(id: string): void {
  enqueue({ topic: 'order-cancelled', payload: JSON.stringify({ id }) });
}

If an event must be published sooner, shorten the producer's interval; do not publish from the request.

Try it

The playground holds this page's repository and rule. Run cruises the files in this browser, as rulebearing cruise does in repo/; change the rule and run it again, or pick another output type.

Everything runs in this browser, with the same library crates as the rulebearing binary: nothing you paste, choose or drop is uploaded or sent anywhere. The configuration is YAML or JSON. A JavaScript configuration is not accepted: the browser build has no JavaScript engine (ADR-0067). An extends of a file cannot be followed; the bundled presets resolve as on the command line. Errors are the module's own words, shown as they come.

  • A cruise result: paste the JSON that rulebearing cruise -T json (or dependency-cruiser) wrote, and Run evaluates the configuration's rules over it, as rulebearing cruise --graph graph.json -T <output type> does. This is the way in for .NET: assemblies are not accepted, since reading them needs a build a browser cannot run, so paste the cruise result the command line wrote for the solution.
  • Source files: choose a folder or files, or drop them, holding TypeScript and JavaScript or Python, and Run extracts and evaluates them, as rulebearing cruise -T <output type> does in that folder. Paths are taken from the chosen folder. The language is the one picked, or, left on Infer from the files, the one with more source files outside node_modules and virtual environments (a tie asks you to pick). Files that are not UTF-8 text, files over 1 MiB, anything past 32 MiB or 20,000 files in all, and version-control folders are skipped, and the note under the pickers names them. A configuration file at the folder's root (rulebearing.yaml, rulebearing.yml, rulebearing.json or .dependency-cruiser.json) is loaded into the configuration box. What needs the disk or a process is refused with the reason: the Node sidecar, Yarn Plug'n'Play, a ratchet's budget file, and a path outside the folder.
  • Explain prints what rulebearing explain <rule> --graph graph.json prints for the named rule over the current cruise result: the pasted one, or the json cruise of the chosen files.

Examples:

Loading the rulebearing module.