1. Product surfaces, distribution and uniform entry points
Context
bpmn-to-code reaches its users in several ways: as a build plugin, in the browser, as a test library and as a small library that generated code depends on. All of them have to produce the same result for the same BPMN files, and a user should not have to resolve a diamond of internal artifacts to get there.
Decision
Published surfaces. Twelve things are shipped:
- the Gradle plugin
io.miragon.bpmn-to-code-gradle(Gradle Plugin Portal), - the Maven plugin
io.miragon:bpmn-to-code-maven, - the testing library
io.miragon:bpmn-to-code-testing(record 10), - the runtime library
io.miragon:bpmn-to-code-runtime(record 8), - the web application as the Docker image
miragon/bpmn-to-code-web(record 12), - the command-line tool as one archive per platform on the GitHub release, with its own Java runtime, and as the Homebrew formula
miragon/tap/bpmn-to-code(record 14), - the same tool as the .NET tool
Miragon.BpmnToCodeon nuget.org (record 14), - the C# runtime package
Miragon.BpmnToCode.Runtimeon nuget.org (record 7), - the MSBuild integration
Miragon.BpmnToCode.MSBuildon nuget.org, which generates duringdotnet buildthrough the .NET tool (record 14), - the same tool as the npm package
@miragon/bpmn-to-code(record 14), - the TypeScript runtime package
@miragon/bpmn-to-code-runtimeon npm (record 7), - the agent skills in
bpmn-to-code-skills/, a Claude Code plugin installed through the marketplace manifest in the repository root.
Core is not published. bpmn-to-code-core has no coordinates. The Gradle plugin, the Maven plugin and the testing library declare it compileOnly and copy its compiled classes into their own jar; core's third-party dependencies are declared again in each of them. Nothing is relocated. Only the web module and the command-line tool use core as an ordinary project dependency.
What of core is public. Core's domain package is a supported API for exactly one purpose: writing custom validation rules against bpmn-to-code-testing. Everything else in core is internal, including the inbound adapters the wrappers call.
Entry points are thin and uniform. Gradle, Maven, web and the command-line tool (record 14) contain no generation logic. They translate their configuration into a call of one of core's inbound *Plugin classes (record 2), so a feature behaves identically everywhere. An option that shapes the output belongs into the BPMN model, not into a plugin setting: variantName is a process extension property (record 5), and generation accepts no validation settings from any entry point.
An entry point may differ only in what its host requires:
- Gradle takes part in up-to-date checks and the build cache. It asks core which files a pattern matches, so Gradle and the generator can never see different inputs, and it declares the generated directory as an output only when it lies below the build directory.
- Gradle adds the runtime library in its own version as an
implementationdependency once thejavaplugin is applied. Maven users declare it themselves. - Neither plugin binds itself to the build lifecycle: Gradle only registers tasks, the Maven goals have no default phase.
- The web application fixes the package name and caps the number of files per request.
Agent skills carry the library version. They are not a Gradle module; release-please bumps the version in their plugin.json with every release (record 15).
Parameters and defaults are listed in Configuration.
Consequences
- Users add one plugin or one test dependency and get a working generator; there is no core version to align.
- The classes of
io.miragon.bpmn.domainexist in three published jars. Changing a domain type is a change to the testing library's API (record 11). - FreeMarker and
camunda-bpmn-modelreach the user's build classpath,camunda-bpmn-modeland AssertJ the test classpath, all under their original package names. - Core's dependency list is maintained in every wrapper by hand. A dependency a wrapper forgets fails only when the published plugin runs, which is why the Gradle module resolves its own published plugin in a smoke test.
- A skill fix reaches users only with the next release.
- A setting that exists in one entry point only is a defect, not a feature.
Rejected alternatives
- Publish core as its own artifact. It would make all of core a public contract and add a fourth coordinate users have to keep in step.
- Shade and relocate core's dependencies. Custom rules compile against
io.miragon.bpmn.domain, and relocation would complicate exactly the jar that exposes it. Not needed while the dependency set is this small. - Behaviour switches as plugin settings.
enableVariantswas one. It had to be plumbed through every entry point and never reached all of them: the validation tasks and the testing library had no such setting and behaved differently from generation.