Software Architecture Document#
Document ID |
DHF-004 |
|---|---|
Version |
1.0 |
Date |
2026-03-22 |
Author |
pacs008 Engineering |
Status |
Released |
ISO 13485 Clause |
7.3.4 (Design and Development Review) |
1. Module Architecture#
The pacs008 library is organized into 14 packages, each with a single responsibility:
Package |
Responsibility |
|---|---|
|
FastAPI REST API application ( |
|
Click-based command-line interface ( |
|
SWIFT compliance: charset validation (Z/z set), field length
enforcement, transliteration ( |
|
Application context singleton for logger configuration
( |
|
Main orchestration: |
|
CSV data loading ( |
|
Universal data loader dispatch: |
|
SQLite loading ( |
|
JSON and JSONL loading with streaming support
( |
|
Apache Parquet loading with streaming support
( |
|
Path traversal protection ( |
|
BIC validation ( |
|
XML generation ( |
|
13 versioned directories, each containing a Jinja2 template
( |
Top-level modules:
__init__.py— Public API exports (process_files,generate_xml_string,PaymentValidationError,DataSourceError,__version__)__main__.py—main()entry point forpython -m pacs008constants.py—valid_xml_typeslist,BASE_DIR,SCHEMAS_DIR,TEMPLATES_DIRexceptions.py— Exception hierarchylogging_schema.py— Structured logging (Events,Fields,log_event())
2. Data Flow#
The primary data flow through the system follows this path:
User Input
│
▼
process_files(xml_message_type, template, schema, data_source)
│
├─▶ _validate_inputs() # Check message type + file paths
│
├─▶ _determine_data_source_type() # Detect format from extension/type
│
├─▶ _load_data()
│ │
│ └─▶ load_payment_data() # Universal dispatcher
│ │
│ ├─▶ load_csv_data() # .csv
│ ├─▶ load_json_data() # .json
│ ├─▶ load_jsonl_data() # .jsonl
│ ├─▶ load_db_data() # .db
│ ├─▶ load_parquet_data() # .parquet
│ └─▶ pass-through # list/dict
│
├─▶ register_namespaces() # Version-specific XML namespaces
│
└─▶ _generate_and_log()
│
└─▶ generate_xml_string(data, message_type, template, schema)
│
├─▶ validate_path() # Path jail check
├─▶ xml_data_preparers[type]() # Version dispatch
├─▶ Environment(autoescape=True) # Jinja2 rendering
├─▶ template.render(**data) # XML string output
└─▶ validate_xml_string_via_xsd() # XSD validation
│
└─▶ defusedxml.ElementTree # Safe XML parsing
│
└─▶ Validated XML string returned
3. Version Dispatch Strategy#
Version-specific XML generation is handled through a dispatch dictionary in
pacs008/xml/generate_xml.py:
xml_data_preparers = {
"pacs.008.001.01": _prepare_xml_data_v01,
"pacs.008.001.02": _prepare_xml_data_v02_to_v04,
"pacs.008.001.03": _prepare_xml_data_v02_to_v04,
"pacs.008.001.04": _prepare_xml_data_v02_to_v04,
"pacs.008.001.05": _prepare_xml_data_v05_to_v06,
"pacs.008.001.06": _prepare_xml_data_v05_to_v06,
"pacs.008.001.07": _prepare_xml_data_v07,
"pacs.008.001.08": _prepare_xml_data_v08_to_v09,
"pacs.008.001.09": _prepare_xml_data_v08_to_v09,
"pacs.008.001.10": _prepare_xml_data_v10_to_v12,
"pacs.008.001.11": _prepare_xml_data_v10_to_v12,
"pacs.008.001.12": _prepare_xml_data_v10_to_v12,
"pacs.008.001.13": _prepare_xml_data_v13,
}
Version groupings and their distinguishing features:
Preparer |
Versions |
Distinguishing Features |
|---|---|---|
|
v01 |
|
|
v02, v03, v04 |
BIC/BICFI transition, |
|
v05, v06 |
Full |
|
v07 |
|
|
v08, v09 |
Adds UETR (Unique End-to-End Transaction Reference) |
|
v10, v11, v12 |
Adds mandate information ( |
|
v13 |
Adds expiry date-time ( |
Adding support for future pacs.008 versions requires only:
Adding a new version-specific Jinja2 template and XSD schema in
templates/Implementing a data preparer function (or reusing an existing one)
Adding an entry to the
xml_data_preparersdictionaryAdding the version string to
valid_xml_typesinconstants.py
4. Exception Hierarchy#
Pacs008Error (base)
├── PaymentValidationError
│ ├── InvalidIBANError
│ │ (fields: message, iban, field, reason)
│ ├── InvalidBICError
│ │ (fields: message, bic, field, reason)
│ └── MissingRequiredFieldError
│ (fields: message, field, row_number, required_fields)
├── XMLGenerationError
│ (Jinja2 rendering failures, XSD validation failures)
├── ConfigurationError
│ (invalid message types, missing env vars, config file errors)
├── DataSourceError
│ (file not found, DB errors, unsupported formats)
└── SchemaValidationError (alias: XSDValidationError)
(fields: message, errors: list)
All exceptions inherit from Pacs008Error to enable catch-all handling at
API and CLI boundaries.
5. Security Architecture#
5.1 XML External Entity (XXE) Prevention#
Module:
pacs008/xml/validate_via_xsd.pyControl: All XML parsing uses
defusedxml.ElementTreeinstead of the standard library’sxml.etree.ElementTreeProtection: Prevents XML bombs, entity expansion attacks, and external entity injection
Requirement: NFR-101
5.2 Path Traversal Protection#
Module:
pacs008/security/path_validator.pyControl:
validate_path(untrusted_path, must_exist, base_dir)resolves paths withos.path.realpath()and rejects any path containing..or resolving outside allowed directoriesAllowed directories: current working directory,
tempfile.gettempdir(),/var/tmp(Unix only)Requirement: NFR-102
5.3 SQL Input Validation#
Module:
pacs008/db/load_db_data.py,pacs008/db/load_db_data_streaming.pyControl: Table name validation with regex pattern matching; parameterized queries where applicable
Requirement: NFR-102
5.4 Log Sanitization#
Module:
pacs008/security/path_validator.py,pacs008/logging_schema.pyControl:
sanitize_for_log(user_input, max_length=100)strips control characters and truncates input before log emission; automatic PII redaction for IBAN, BIC, and personal namesRequirement: NFR-103
5.5 Template Injection Prevention#
Module:
pacs008/xml/generate_xml.pyControl:
Environment(loader=FileSystemLoader(...), autoescape=True)Protection: All template variables are auto-escaped, preventing server-side template injection (SSTI)
Requirement: NFR-104
5.6 Container Security#
Module:
DockerfileControl: Application runs as
appuser(non-root), slim base image, health check endpointRequirement: NFR-105
6. Interface Specifications#
6.1 Python API#
# Primary entry point — full pipeline
process_files(
xml_message_type: str, # e.g. "pacs.008.001.05"
xml_template_file_path: str, # path to Jinja2 template
xsd_schema_file_path: str, # path to XSD schema
data_file_path: Union[str, list, dict], # data source
) -> None
# Low-level — returns XML string without file I/O
generate_xml_string(
data: Union[list, dict],
payment_initiation_message_type: str,
xml_template_path: str,
xsd_schema_path: str,
) -> str
6.2 CLI#
pacs008 -t <message-type> -m <template> -s <schema> -d <data>
[-o <output-dir>] [--dry-run] [-v]
Exit codes: 0 (success), 1 (validation/processing error), 2 (invalid arguments)
6.3 REST API#
Method |
Endpoint |
Purpose |
|---|---|---|
GET |
|
Health check |
POST |
|
Validate payment data without generating XML |
POST |
|
Generate XML synchronously |
POST |
|
Submit async XML generation job |
GET |
|
Poll async job status |
DELETE |
|
Cancel async job |
GET |
|
Download generated XML from completed job |
7. Design Decisions and Rationale#
Decision |
Choice |
Rationale |
|---|---|---|
Template engine |
Jinja2 |
Mature, well-documented, supports autoescape; separates XML structure from data logic |
XML parser |
defusedxml |
Drop-in replacement for stdlib with XXE protection; no API changes required |
Version dispatch |
Dictionary of functions |
O(1) lookup, extensible without modifying existing code, each version group is isolated |
Validation library |
xmlschema + jsonschema |
Official XSD/JSON Schema implementations; comprehensive error reporting |
CLI framework |
Click |
Declarative option/argument syntax, automatic help text, composable commands |
REST framework |
FastAPI |
Async support, automatic OpenAPI docs, Pydantic validation, type hints |
Data formats |
CSV, JSON, JSONL, SQLite, Parquet |
Covers spreadsheet exports (CSV), API responses (JSON/JSONL), databases (SQLite), and analytics pipelines (Parquet) |
Streaming support |
Chunked iterators |
Bounds memory usage for large datasets; configurable chunk size |
Exception hierarchy |
Single base class |
Enables catch-all at boundaries while preserving specific error context |
Path security |
Allowlist directories |
Defense-in-depth; even if application logic is wrong, path jail prevents traversal |