Skip to content

DTCE Sample Application

Build an invoice-pricing app that turns checkout carts into invoices in background workers. You use Winter Boot for the application runtime and the Winter DTCE module from Winter Modules for distributed task execution. You test the whole flow locally with the shared queue and disk store, so no Docker or external service is needed.

You need PHP 8.5 or later with the swoole and pcntl extensions.

Terminal window
pecl install swoole

The sample uses this layout:

dtce/
├── bin/
│ └── application.php # Application entry point
├── config/
│ ├── application.yml # Winter Boot application config
│ └── dtce-config.yml # DTCE task, worker, and queue config
├── src/
│ ├── DtceSampleApplication.php # Main application class
│ ├── controller/
│ │ └── InvoiceDemoController.php # REST endpoints that submit tasks and jobs
│ └── task/
│ └── InvoiceTotalWorker.php # DTCE worker that prices one order
└── composer.json # Dependencies

Require the framework and the modules package:

Terminal window
composer require suvera/winter-boot suvera/winter-modules

Switch between the five source files. Each tab shows the exact file from the sample.

The single entry point. It points Winter Boot at the config directory and at the sample namespace.

<?php
namespace dev\example;
use dev\winterframework\stereotype\WinterBootApplication;
#[WinterBootApplication(
configDirectory: [__DIR__ . "/../config"],
scanNamespaces: [
['dev\\example', __DIR__ . '']
]
)]
class DtceSampleApplication {
public static function main(): void {
$winterApp = new \dev\winterframework\core\app\WinterWebSwooleApplication();
$winterApp->run(self::class);
}
}

Switch between the two config files. application.yml enables the module and sets the shared-queue port, and dtce-config.yml defines the invoiceTotal task with its worker, disk store, and shared queue.

The full sample file registers DtceModule and sets the server and app identity. winter.queue.port is required by the shared queue used below:

server:
port: 8080
address: 0.0.0.0
context-path: /
winter:
application:
name: DTCE Sample Application
id: dtce-sample-app
version: 1.0.0
queue:
port: 7881
modules:
- module: dev\winterframework\dtce\DtceModule
enabled: true
configFile: dtce-config.yml

See Configuration for every application.yml key.

Start the app, price one order, settle a batch, then check the output file.

1. Start the application:

Terminal window
composer install
php bin/application.php

The DTCE workers start processing the invoiceTotal queue as soon as the app boots.

2. Price a single order:

Terminal window
curl -X POST http://localhost:8080/invoicedemo/price \
--data-urlencode 'items=[{"price":999,"qty":1},{"price":49.5,"qty":2}]' \
--data-urlencode 'discountPct=10' \
--data-urlencode 'taxPct=18'

You get the invoice breakdown back, for example:

{"success":true,"invoice":{"subtotal":1098,"discount":109.8,"tax":177.88,"total":1166.08}}

3. Settle a batch of orders as one job:

Terminal window
curl "http://localhost:8080/invoicedemo/settle"

You get one invoice per order in the job, for example:

{"success":true,"invoices":[{"subtotal":999,"discount":0,"tax":179.82,"total":1178.82},{"subtotal":99,"discount":9.9,"tax":16.04,"total":105.14},{"subtotal":109.95,"discount":5.5,"tax":0,"total":104.45}]}

4. Verify the worker output:

Terminal window
cat /tmp/dtce-invoices.txt

You see one line per priced order, for example:

[2026-09-10 12:00:01] Items: 2 | Subtotal: 1098.00 | Discount: 109.80 | Tax: 177.88 | Total: 1166.08
[2026-09-10 12:00:02] Items: 1 | Subtotal: 999.00 | Discount: 0.00 | Tax: 179.82 | Total: 1178.82
  • Read the DTCE module for the async APIs (addTask, addJob, taskStatus, stopTask), persistent backends, and full configuration.
  • Browse all Libraries when you need Kafka, SQS, Redis, or Doctrine in the same app.