Actual disadvantages of using Dynamic Workflows/Activities based on configuration metadata instead of complete workflow as code

We’re looking into using Temporal for a transaction processing system, and I’m trying to figure out if we’re approaching the design in the right way.

Our system is pretty metadata-driven. We have a fairly large domain model built using DDD, and keeping that model intact is important to us. Since we have a layered architecture, it feels like services from different layers could potentially be modelled as Temporal Activities.

That’s what led us to DynamicActivity in the Java SDK. The idea is that our metadata can determine which activity needs to run, rather than having to define every possible activity upfront.

But I’m not sure what we’re giving up by doing this.

For anyone who has built something similar with Temporal:

  • Are there significant downsides to using DynamicActivity heavily?

  • Does it make things like replay, versioning, debugging, observability, or testing harder?

  • Would you keep the domain/metadata model completely separate from the Temporal workflow model and have some kind of mapping between them?

  • Is there anything else we should be thinking about that might not be obvious from the docs?

We’re fairly new to workflow orchestration, so I’d really appreciate any practical experience or lessons learned from people who have dealt with DDD + metadata-driven workflows + Temporal.

What do you mean by metadata-driven? Are you using some sort of configuration based language to define your business logic? Can you give an example how you define some business workflow?

Hi @maxin thanks for the reply,
This here is a ValueFlow object.
{
“valueFlowId”: “remittance-v1”,
“valueFlowName”: “Remittance”,
“version”: “1.0.0”,

“status”: “ACTIVE”,
“dateFrom”: “2026-01-01”,
“dateTo”: “2099-12-31”,
“stageFlows”: [
{
“stageFlowId”: “sf-main-seq”,
“order”: “SEQUENTIAL”,
“dependencyStageFlowId”: null,
“stages”: [
{
“stageId”: “ENRICHMENT”,
“stageName”: “ENRICHMENT”,
“action”: “EXECUTE”,
“stageKind”: “STANDARD”,
“consumeEvent”: {
“eventId”: “EnrichmentRequested”,
“eventName”: “EnrichmentRequested”,
“eventType”: “DOMAIN”,
“eventProperties”: []
},
“publishEvent”: {
“eventId”: “EnrichmentCompleted”,
“eventName”: “EnrichmentCompleted”,
“eventType”: “DOMAIN”,
“eventProperties”: []
},
“processorFlows”: [
{
“processorFlowId”: “enrich-pf”,
“order”: “SEQUENTIAL”,
“dependencyProcessorFlowId”: null,
“processors”: [
{
“processorId”: “MockEnrichmentProcessor”,
“processorName”: “MockEnrichmentProcessor”,
“valueComponent”: {
“key”: “AR-MockEnrichmentProcessor”,
“valueComponentKey”: “VC-MockEnrichmentProcessor”
},
“applicationResource”: {
“key”: “AR-MockEnrichmentProcessor”
},
“feedsVirtualAccount”: false
}
]
}
],
“feedsVirtualAccount”: false,
“capabilities”: [],
“enterpriseApiKeys”: [],
“spiServiceKey”: null
},
{
“stageId”: “VALIDATION”,
“stageName”: “VALIDATION”,
“action”: “EXECUTE”,
“stageKind”: “STANDARD”,
“consumeEvent”: {
“eventId”: “ValidationRequested”,
“eventName”: “ValidationRequested”,
“eventType”: “DOMAIN”,
“eventProperties”: []
},
“publishEvent”: {
“eventId”: “ValidationCompleted”,
“eventName”: “ValidationCompleted”,
“eventType”: “DOMAIN”,
“eventProperties”: []
},
“processorFlows”: [
{
“processorFlowId”: “validation-pf”,
“order”: “SEQUENTIAL”,
“dependencyProcessorFlowId”: null,
“processors”: [
{
“processorId”: “MockValidationFormatProcessor”,
“processorName”: “MockValidationFormatProcessor”,
“valueComponent”: {
“key”: “AR-MockValidationFormatProcessor”,
“valueComponentKey”: “VC-MockValidationFormatProcessor”
},
“applicationResource”: {
“key”: “AR-MockValidationFormatProcessor”
},
“feedsVirtualAccount”: false
},
{
“processorId”: “MockValidationBusinessProcessor”,
“processorName”: “MockValidationBusinessProcessor”,
“valueComponent”: {
“key”: “AR-MockValidationBusinessProcessor”,
“valueComponentKey”: “VC-MockValidationBusinessProcessor”
},
“applicationResource”: {
“key”: “AR-MockValidationBusinessProcessor”
},
“feedsVirtualAccount”: false
}
]
}
],
“feedsVirtualAccount”: false,
“capabilities”: [],
“enterpriseApiKeys”: [],
“spiServiceKey”: null
},
{
“stageId”: “COMPLIANCE”,
“stageName”: “COMPLIANCE”,
“action”: “EXECUTE”,
“stageKind”: “STANDARD”,
“awaitSignal”: true,
“signalName”: “release-hold”,
“signalTimeout”: “PT30M”,
“consumeEvent”: {
“eventId”: “ComplianceRequested”,
“eventName”: “ComplianceRequested”,
“eventType”: “DOMAIN”,
“eventProperties”: []
},
“publishEvent”: {
“eventId”: “ComplianceCompleted”,
“eventName”: “ComplianceCompleted”,
“eventType”: “DOMAIN”,
“eventProperties”: []
},
“processorFlows”: [
{
“processorFlowId”: “compliance-pf”,
“order”: “PARALLEL”,
“dependencyProcessorFlowId”: null,
“processors”: [
{
“processorId”: “MockComplianceAmlProcessor”,
“processorName”: “MockComplianceAmlProcessor”,
“valueComponent”: {
“key”: “AR-MockComplianceAmlProcessor”,
“valueComponentKey”: “VC-MockComplianceAmlProcessor”
},
“applicationResource”: {
“key”: “AR-MockComplianceAmlProcessor”
},
“feedsVirtualAccount”: false
},
{
“processorId”: “MockComplianceSanctionsProcessor”,
“processorName”: “MockComplianceSanctionsProcessor”,
“valueComponent”: {
“key”: “AR-MockComplianceSanctionsProcessor”,
“valueComponentKey”: “VC-MockComplianceSanctionsProcessor”
},
“applicationResource”: {
“key”: “AR-MockComplianceSanctionsProcessor”
},
“feedsVirtualAccount”: false
}
]
}
],
“feedsVirtualAccount”: false,
“capabilities”: [],
“enterpriseApiKeys”: [],
“spiServiceKey”: null
},
{
“stageId”: “PRICING_FX”,
“stageName”: “PRICING_FX”,
“action”: “EXECUTE”,
“stageKind”: “STANDARD”,
“consumeEvent”: {
“eventId”: “PricingRequested”,
“eventName”: “PricingRequested”,
“eventType”: “DOMAIN”,
“eventProperties”: []
},
“publishEvent”: {
“eventId”: “PricingCompleted”,
“eventName”: “PricingCompleted”,
“eventType”: “DOMAIN”,
“eventProperties”: []
},
“processorFlows”: [
{
“processorFlowId”: “pricing-pf”,
“order”: “SEQUENTIAL”,
“dependencyProcessorFlowId”: null,
“processors”: [
{
“processorId”: “MockPricingProcessor”,
“processorName”: “MockPricingProcessor”,
“valueComponent”: {
“key”: “AR-MockPricingProcessor”,
“valueComponentKey”: “VC-MockPricingProcessor”
},
“applicationResource”: {
“key”: “AR-MockPricingProcessor”
},
“feedsVirtualAccount”: false
}
]
}
],
“feedsVirtualAccount”: false,
“capabilities”: [],
“enterpriseApiKeys”: [],
“spiServiceKey”: null
},
{
“stageId”: “FUNDING”,
“stageName”: “FUNDING”,
“action”: “EXECUTE”,
“stageKind”: “STANDARD”,
“consumeEvent”: {
“eventId”: “FundingRequested”,
“eventName”: “FundingRequested”,
“eventType”: “DOMAIN”,
“eventProperties”: []
},
“publishEvent”: {
“eventId”: “FundingCompleted”,
“eventName”: “FundingCompleted”,
“eventType”: “DOMAIN”,
“eventProperties”: []
},
“processorFlows”: [
{
“processorFlowId”: “funding-pf”,
“order”: “SEQUENTIAL”,
“dependencyProcessorFlowId”: null,
“processors”: [
{
“processorId”: “MockFundingProcessor”,
“processorName”: “MockFundingProcessor”,
“valueComponent”: {
“key”: “AR-MockFundingProcessor”,
“valueComponentKey”: “VC-MockFundingProcessor”
},
“applicationResource”: {
“key”: “AR-MockFundingProcessor”
},
“feedsVirtualAccount”: false
}
]
}
],
“feedsVirtualAccount”: true,
“capabilities”: [
“ENTERPRISE_SERVICE”
],
“enterpriseApiKeys”: [
“virtual-account-feed”
],
“spiServiceKey”: null
},
{
“stageId”: “ROUTING”,
“stageName”: “ROUTING”,
“action”: “EXECUTE”,
“stageKind”: “STANDARD”,
“consumeEvent”: {
“eventId”: “RoutingRequested”,
“eventName”: “RoutingRequested”,
“eventType”: “DOMAIN”,
“eventProperties”: []
},
“publishEvent”: {
“eventId”: “RoutingCompleted”,
“eventName”: “RoutingCompleted”,
“eventType”: “DOMAIN”,
“eventProperties”: []
},
“processorFlows”: [
{
“processorFlowId”: “routing-pf”,
“order”: “SEQUENTIAL”,
“dependencyProcessorFlowId”: null,
“processors”: [
{
“processorId”: “MockRoutingProcessor”,
“processorName”: “MockRoutingProcessor”,
“valueComponent”: {
“key”: “AR-MockRoutingProcessor”,
“valueComponentKey”: “VC-MockRoutingProcessor”
},
“applicationResource”: {
“key”: “AR-MockRoutingProcessor”
},
“feedsVirtualAccount”: false
}
]
}
],
“feedsVirtualAccount”: true,
“capabilities”: [
“ENTERPRISE_SERVICE”,
“SPI_SELECTION”
],
“enterpriseApiKeys”: [
“virtual-account-feed”
],
“spiServiceKey”: “routing-spi”
},
{
“stageId”: “PARTNER_DISPATCH”,
“stageName”: “PARTNER_DISPATCH”,
“action”: “EXECUTE”,
“stageKind”: “STANDARD”,
“consumeEvent”: {
“eventId”: “PartnerDispatchRequested”,
“eventName”: “PartnerDispatchRequested”,
“eventType”: “DOMAIN”,
“eventProperties”: []
},
“publishEvent”: {
“eventId”: “PartnerDispatchCompleted”,
“eventName”: “PartnerDispatchCompleted”,
“eventType”: “DOMAIN”,
“eventProperties”: []
},
“processorFlows”: [
{
“processorFlowId”: “dispatch-pf”,
“order”: “SEQUENTIAL”,
“dependencyProcessorFlowId”: null,
“processors”: [
{
“processorId”: “MockPartnerDispatchProcessor”,
“processorName”: “MockPartnerDispatchProcessor”,
“valueComponent”: {
“key”: “AR-MockPartnerDispatchProcessor”,
“valueComponentKey”: “VC-MockPartnerDispatchProcessor”
},
“applicationResource”: {
“key”: “AR-MockPartnerDispatchProcessor”
},
“feedsVirtualAccount”: false
}
]
}
],
“feedsVirtualAccount”: false,
“capabilities”: [
“OUTBOX_EXTERNAL”,
“SPI_SELECTION”
],
“enterpriseApiKeys”: [],
“spiServiceKey”: “partner-dispatch-spi”
},
{
“stageId”: “SETTLEMENT”,
“stageName”: “SETTLEMENT”,
“action”: “EXECUTE”,
“stageKind”: “STANDARD”,
“consumeEvent”: {
“eventId”: “SettlementRequested”,
“eventName”: “SettlementRequested”,
“eventType”: “DOMAIN”,
“eventProperties”: []
},
“publishEvent”: {
“eventId”: “SettlementCompleted”,
“eventName”: “SettlementCompleted”,
“eventType”: “DOMAIN”,
“eventProperties”: []
},
“processorFlows”: [
{
“processorFlowId”: “settlement-pf”,
“order”: “SEQUENTIAL”,
“dependencyProcessorFlowId”: null,
“processors”: [
{
“processorId”: “MockSettlementProcessor”,
“processorName”: “MockSettlementProcessor”,
“valueComponent”: {
“key”: “AR-MockSettlementProcessor”,
“valueComponentKey”: “VC-MockSettlementProcessor”
},
“applicationResource”: {
“key”: “AR-MockSettlementProcessor”
},
“feedsVirtualAccount”: false
}
]
}
],
“feedsVirtualAccount”: false,
“capabilities”: [],
“enterpriseApiKeys”: [],
“spiServiceKey”: null
}
]
},
{
“stageFlowId”: “sf-post-settlement-parallel”,
“order”: “PARALLEL”,
“dependencyStageFlowId”: “sf-main-seq”,
“stages”: [
{
“stageId”: “RECONCILIATION”,
“stageName”: “RECONCILIATION”,
“action”: “EXECUTE”,
“stageKind”: “STANDARD”,
“consumeEvent”: {
“eventId”: “ReconciliationRequested”,
“eventName”: “ReconciliationRequested”,
“eventType”: “DOMAIN”,
“eventProperties”: []
},
“publishEvent”: {
“eventId”: “ReconciliationCompleted”,
“eventName”: “ReconciliationCompleted”,
“eventType”: “DOMAIN”,
“eventProperties”: []
},
“processorFlows”: [
{
“processorFlowId”: “recon-pf”,
“order”: “SEQUENTIAL”,
“dependencyProcessorFlowId”: null,
“processors”: [
{
“processorId”: “MockReconciliationProcessor”,
“processorName”: “MockReconciliationProcessor”,
“valueComponent”: {
“key”: “AR-MockReconciliationProcessor”,
“valueComponentKey”: “VC-MockReconciliationProcessor”
},
“applicationResource”: {
“key”: “AR-MockReconciliationProcessor”
},
“feedsVirtualAccount”: false
}
]
}
],
“feedsVirtualAccount”: false,
“capabilities”: [],
“enterpriseApiKeys”: [],
“spiServiceKey”: null
},
{
“stageId”: “NOTIFICATION”,
“stageName”: “NOTIFICATION”,
“action”: “EXECUTE”,
“stageKind”: “STANDARD”,
“consumeEvent”: {
“eventId”: “NotificationRequested”,
“eventName”: “NotificationRequested”,
“eventType”: “DOMAIN”,
“eventProperties”: []
},
“publishEvent”: {
“eventId”: “NotificationCompleted”,
“eventName”: “NotificationCompleted”,
“eventType”: “DOMAIN”,
“eventProperties”: []
},
“processorFlows”: [
{
“processorFlowId”: “notify-pf”,
“order”: “SEQUENTIAL”,
“dependencyProcessorFlowId”: null,
“processors”: [
{
“processorId”: “MockNotificationProcessor”,
“processorName”: “MockNotificationProcessor”,
“valueComponent”: {
“key”: “AR-MockNotificationProcessor”,
“valueComponentKey”: “VC-MockNotificationProcessor”
},
“applicationResource”: {
“key”: “AR-MockNotificationProcessor”
},
“feedsVirtualAccount”: false
}
]
}
],
“feedsVirtualAccount”: false,
“capabilities”: [],
“enterpriseApiKeys”: [],
“spiServiceKey”: null
}
]
},
{
“stageFlowId”: “sf-post-settlement-merge”,
“order”: “SEQUENTIAL”,
“dependencyStageFlowId”: “sf-post-settlement-parallel”,
“stages”: [
{
“stageId”: “POST_SETTLEMENT_MERGE”,
“stageName”: “POST_SETTLEMENT_MERGE”,
“action”: “MERGE”,
“stageKind”: “MERGE”,
“consumeEvent”: {
“eventId”: “MergeRequested”,
“eventName”: “MergeRequested”,
“eventType”: “DOMAIN”,
“eventProperties”: []
},
“publishEvent”: {
“eventId”: “PostSettlementMergeCompleted”,
“eventName”: “PostSettlementMergeCompleted”,
“eventType”: “DOMAIN”,
“eventProperties”: []
},
“processorFlows”: [
{
“processorFlowId”: “merge-pf”,
“order”: “SEQUENTIAL”,
“dependencyProcessorFlowId”: null,
“processors”: [
{
“processorId”: “MockMergeProcessor”,
“processorName”: “MockMergeProcessor”,
“valueComponent”: {
“key”: “AR-MockMergeProcessor”,
“valueComponentKey”: “VC-MockMergeProcessor”
},
“applicationResource”: {
“key”: “AR-MockMergeProcessor”
},
“feedsVirtualAccount”: false
}
]
}
],
“feedsVirtualAccount”: false,
“capabilities”: [],
“enterpriseApiKeys”: [],
“spiServiceKey”: null
}
]
},
{
“stageFlowId”: “sf-completion”,
“order”: “SEQUENTIAL”,
“dependencyStageFlowId”: “sf-post-settlement-merge”,
“stages”: [
{
“stageId”: “COMPLETION”,
“stageName”: “COMPLETION”,
“action”: “EXECUTE”,
“stageKind”: “STANDARD”,
“consumeEvent”: {
“eventId”: “CompletionRequested”,
“eventName”: “CompletionRequested”,
“eventType”: “DOMAIN”,
“eventProperties”: []
},
“publishEvent”: {
“eventId”: “TransactionCompleted”,
“eventName”: “TransactionCompleted”,
“eventType”: “DOMAIN”,
“eventProperties”: []
},
“processorFlows”: [
{
“processorFlowId”: “completion-pf”,
“order”: “SEQUENTIAL”,
“dependencyProcessorFlowId”: null,
“processors”: [
{
“processorId”: “MockCompletionProcessor”,
“processorName”: “MockCompletionProcessor”,
“valueComponent”: {
“key”: “AR-MockCompletionProcessor”,
“valueComponentKey”: “VC-MockCompletionProcessor”
},
“applicationResource”: {
“key”: “AR-MockCompletionProcessor”
},
“feedsVirtualAccount”: false
}
]
}
],
“feedsVirtualAccount”: false,
“capabilities”: [],
“enterpriseApiKeys”: [],
“spiServiceKey”: null
}
]
}
]
}

This is the structure we have currently, It is a work in progress but we are trying to map each ValueFlow object like the one i have here as a DynamicWorkflow and the rough mapping we have for an activity is every Processor inside the stageFlows.stages.processorFlows.processors becomes a temporal activity. Here, each processor is linked to a applicationResource which basically a reference to a api/interface available within our modules. (implementations could have external services called)

I apologize if this does not make sense, I am trying to compress information here that i don’t completely comprehend yet but this is the best i could do. Thank you for reading through and trying to help. Anything you add is much appreciated.