A language-agnostic post-processing toolkit that turns each downstream operation—hashing, OCR, thumbnails, embeddings—into an independent Unit of Work (UoW). The Go core integrates with simplecontent, while SDKs and transports let you compose resilient pipelines that match your durability and orchestration requirements.
- Unit of Work first: Every processor implements
uow.UoWor an SDK decorator so steps stay isolated, idempotent, and retry-safe. - Bring your own orchestration: Run inline, fan out over a queue, or plug into DBOS/user-defined DAGs; the library remains orchestration-agnostic.
- Zero-copy I/O: Workers stream data via presigned URLs instead of copying payloads through intermediaries.
- Resume anywhere: Persist state after every UoW and rely on idem keys to recover without double effects.
- Pluggable adapters: Swap storage, metadata, bus, logging, and tracing implementations without touching business logic.
- Observability ready: Runners stay lean so you can thread in tracing, logging, or metrics adapters without touching UoWs.
core/– Go domain primitives (contracts, adapters, runners, UoW interface) that applications embed.transports/– Protocol-specific bindings (HTTP callback handler baseline plus an optional NATS bus under thenatsbuild tag that emits CloudEvents envelopes).examples/– Runnable walkthroughs (currently inline hash example) showing how to wire a runner, adapter, and UoW.uows/– Reference UoWs in multiple languages (uows/go,uows/python) for reuse across services.sdk/– Language SDKs that expose decorators/helpers for registering UoWs with their runtimes.docs/– Design notes and contract references that complement this README.
- Install Go 1.18+ and (optionally) Python 3.10+ if you plan to run the Python SDK.
- Build the inline sample:
make build(outputs tobin/inline-example). - Execute tests:
make test(orgo test ./...) to cover unit and async integration scenarios. SetGOCACHE=$(pwd)/.gocache(andGOTOOLCHAIN=localwhen toolchain downloads are blocked) in sandboxed environments. - Run the inline example:
go run ./examples/inlineafter pointingstorage.Putat a reader for your input file. - Try the async workflow:
go run ./examples/asyncto seeAsyncRunnerpublishing to the in-memory bus while a worker updates metadata. - Validate the Python SDK:
PYTHONPATH=sdk/python python3 -m unittest discover -s sdk/python/tests -p 'test_*.py'. - (Optional) Run the NATS demo once a local
nats-serveris running:go run -tags nats ./examples/nats(requiresgo get github.com/nats-io/nats.go). Jobs are wrapped in CloudEvents v1.0 envelopes, so any downstream consumer that speaks CloudEvents can participate.
- Go: Implement
core/uow.UoWand return acontracts.Result. Refer touows/go/hash/hash.gofor a minimal example. - Python: Decorate a function with
@uow("name")fromsdk/python/uow. Keep return payloads JSON-serializable and mirror theResultcontract. - Persist artifacts via the
adapters.Storageinterface and update metadata usingadapters.Metadata.
- Use
core/runner.SyncRunnerfor inline execution inside an API or CLI process. - Use
core/runner.AsyncRunnerwith anadapters.Busimplementation to fan jobs out to external workers. - Compose runners with tracing/logging adapters so cross-cutting concerns stay outside UoW code.
- Add the module:
go get github.com/tendant/simple-process@latestfor Go services, or install the Python SDK (PYTHONPATH=sdk/pythonduring development) for worker code. - Inject adapters that reflect your infrastructure (e.g., S3-backed storage, Dynamo metadata, Kafka/NATS bus) while keeping UoWs oblivious to deployment details.
- Register or import your UoWs (
uows/go/...,uows/python/...) and execute them viaSyncRunner(inline) orAsyncRunner(queue-based) depending on latency and durability needs. - Persist the returned
contracts.Resultby patching metadata, recording artifacts, or chaining additional jobs; use transports/handlers to publish follow-up CloudEvents if required. - Cover the workflow with tests: reuse the async example as an integration template and mirror the Python test command for multi-language validation.
- Implement additional transports (Kafka, NATS, SQS) under
transports/by translating incoming jobs intocontracts.Job. - Provide concrete adapters in
core/adapters/*to integrate with your blob store, metadata service, or observability stack; the in-memory implementations and optional S3/MinIO storage adapter (build tags3) double as reference templates. - Add new reference UoWs under
uows/and document them indocs/so other teams can reuse them. - Keep Job/Result evolution backward compatible; document contract changes in
docs/contracts.mdand version payloads via theJob.Versionfield.
- Start a local broker:
nats-server(Homebrew:brew install nats-server). - Fetch the NATS client once:
go get github.com/nats-io/nats.go@latest. - Publish and consume a job via NATS:
go run -tags nats ./examples/nats. The example wiresAsyncRunnerinto the NATS-backed bus and processes the message with a queue worker using the same in-memory storage used elsewhere in the repository while wrapping every message in a CloudEvents v1.0 envelope.
- Build with the
s3tag to enable the S3-compatible adapter:go build -tags s3 ./...(requires the AWS SDK v2 modules such asgithub.com/aws/aws-sdk-go-v2/configandgithub.com/aws/aws-sdk-go-v2/service/s3). - Configure the adapter via
storage/s3.Config(region, bucket, optional prefix, credentials provider, and optional custom endpoint/path-style) and inject it in place of the in-memory storage when constructing runners or UoWs. - The adapter streams reads via
Get, performs multipart-aware uploads viaPut, and issues presigned download URLs throughPresignGet. - By supplying a custom endpoint and enabling path-style addressing, the same adapter can target MinIO or other S3-compatible backends.
- Jobs published over transports are wrapped in a minimal CloudEvents v1.0 structure (
core/contracts/cloudevent.go). - The event
typeissimpleprocess.job,idmirrorsjob_id, and the payload lives indatawithdatacontenttypeset toapplication/json. - Consumers that already support CloudEvents can leverage headers for routing, retries, and schema enforcement without custom glue.
This repository is still a scaffold: storage adapters, transports, and SDK utilities are minimal. Before production use, flesh out real bus/metadata/logging implementations, complete end-to-end examples, and automate tests across supported languages.