diff --git a/.github/workflows/integration.yml b/.github/workflows/integration.yml
new file mode 100644
index 0000000..332af5a
--- /dev/null
+++ b/.github/workflows/integration.yml
@@ -0,0 +1,111 @@
+name: Testing PHP Code Generator
+on: [push, pull_request]
+
+jobs:
+  tests:
+    strategy:
+      fail-fast: false
+      matrix:
+        php-version:
+          - "7.3"
+          - "7.4"
+        os: [ubuntu-latest]
+        experimental: [false]
+        include:
+          - php-version: "8.0"
+            os: ubuntu-latest
+            experimental: true
+    runs-on: ${{ matrix.os }}
+    name: PHP ${{ matrix.php-version }} Test on ${{ matrix.os }}
+    continue-on-error: ${{ matrix.experimental }}
+    steps:
+      - name: "Checkout"
+        uses: "actions/checkout@v2.3.1"
+
+      - name: "Install PHP"
+        uses: "shivammathur/setup-php@2.4.1"
+        with:
+          php-version: "${{ matrix.php-version }}"
+          coverage: xdebug
+
+      - name: Get composer cache directory
+        id: composercache
+        run: echo "::set-output name=dir::$(composer config cache-files-dir)"
+
+      - name: Cache composer dependencies
+        uses: actions/cache@v2
+        with:
+          path: ${{ steps.composercache.outputs.dir }}
+          # Use composer.json for key, if composer.lock is not committed.
+          # key: ${{ runner.os }}-composer-${{ hashFiles('**/composer.json') }}
+          key: ${{ runner.os }}-composer-${{ hashFiles('**/composer.lock') }}
+          restore-keys: ${{ runner.os }}-composer-
+
+      - name: Install Composer dependencies
+        run: |
+          composer install --no-progress --prefer-dist --optimize-autoloader
+
+      - name: Run Tests
+        run: php vendor/bin/phpunit --coverage-text
+
+  coding-standard:
+    name: Coding Standard
+    runs-on: ubuntu-latest
+    steps:
+      - name: Checkout
+        uses: actions/checkout@v2
+
+      - name: Setup PHP
+        uses: shivammathur/setup-php@v2
+        with:
+          php-version: '7.4'
+
+      - name: Get composer cache directory
+        id: composercache
+        run: echo "::set-output name=dir::$(composer config cache-files-dir)"
+
+      - name: Cache composer dependencies
+        uses: actions/cache@v2
+        with:
+          path: ${{ steps.composercache.outputs.dir }}
+          # Use composer.json for key, if composer.lock is not committed.
+          # key: ${{ runner.os }}-composer-${{ hashFiles('**/composer.json') }}
+          key: ${{ runner.os }}-composer-${{ hashFiles('**/composer.lock') }}
+          restore-keys: ${{ runner.os }}-composer-
+
+      - name: Install dependencies
+        run: composer install --no-progress --prefer-dist --optimize-autoloader
+
+      - name: PHP CodeSniffer
+        run: composer cs
+
+  static-analysis:
+    name: Static Analysis
+    runs-on: ubuntu-latest
+    steps:
+      - name: Checkout
+        uses: actions/checkout@v2
+
+      - name: Setup PHP
+        uses: shivammathur/setup-php@v2
+        with:
+          php-version: '7.4'
+
+      - name: Get composer cache directory
+        id: composercache
+        run: echo "::set-output name=dir::$(composer config cache-files-dir)"
+
+      - name: Cache composer dependencies
+        uses: actions/cache@v2
+        with:
+          path: ${{ steps.composercache.outputs.dir }}
+          # Use composer.json for key, if composer.lock is not committed.
+          # key: ${{ runner.os }}-composer-${{ hashFiles('**/composer.json') }}
+          key: ${{ runner.os }}-composer-${{ hashFiles('**/composer.lock') }}
+          restore-keys: ${{ runner.os }}-composer-
+
+      - name: Install dependencies
+        run: composer install --no-progress --prefer-dist --optimize-autoloader
+
+      - name: Static Analysis using PHPStan
+        run: composer analyse
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 04f0432..87eb55b 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -6,7 +6,7 @@ All notable changes to this project will be documented in this file, in reverse
 
 ### Added
 
-* Nothing
+* Monitoring functionality to display code generation steps and progress.
 
 ### Deprecated
 
@@ -14,7 +14,12 @@ All notable changes to this project will be documented in this file, in reverse
 
 ### Removed
 
-* Nothing
+The following classes are removed (deprecated in 0.2.0).
+
+* Class `\OpenCodeModeling\CodeGenerator\Config\ArrayConfig`
+* Class `\OpenCodeModeling\CodeGenerator\Config\Component`
+* Class `\OpenCodeModeling\CodeGenerator\Config\ComponentCollection`
+* Class `\OpenCodeModeling\CodeGenerator\Config\ComponentList`
 
 ### Fixed
 
diff --git a/composer.json b/composer.json
index 8c02a92..6313735 100644
--- a/composer.json
+++ b/composer.json
@@ -26,6 +26,7 @@
     },
     "require": {
         "php": "^7.3 || ^8.0",
+        "psr/log": "^1.1",
         "symfony/console": "^4.4 || ^5.0 "
     },
     "require-dev": {
diff --git a/docs/book/02-configuration.md b/docs/book/02-configuration.md
index 287d542..a29c6ab 100644
--- a/docs/book/02-configuration.md
+++ b/docs/book/02-configuration.md
@@ -1,7 +1,6 @@
 # Configuration
 
-The code generator CLI can be started through the `bin/ocmcg` executable. This prints the available CLI commands.
-The CLI command `ocmcg:workflow:run` executes the code generation depending on the configuration file. The default
+The code generator is shipped with a CLI which executes the code generation depending on the configuration file. The default
 configuration file name is `open-code-modeling.php.dist` which should be located in the root folder of the application 
 / repository.
 
@@ -9,23 +8,47 @@ This file gets the variable `$workflowContext` provided to configure needed slot
 paths or global data. You have to return an instance of a class which implements `OpenCodeModeling\CodeGenerator\Config\Config` 
 interface.
 
-The following example add some slot data to the workflow context (`$workflowContext->put()`). 
+The following code shows a "Hello World!" example. It prepares the `WorkflowContext` object with the first part of the
+greeting under slot name `part_one`. The workflow consists of two steps. Step *one* adds the second part of the greeting
+to the given input from slot name `part_one`. The returned value is stored under the slot name `greeting`. Step *two*  
+prints the string of the input slot name `greeting`. It doesn't has an output slot name.
+
 ```
+use OpenCodeModeling\CodeGenerator;
+
 /** @var CodeGenerator\Workflow\WorkflowContext $workflowContext */
-$workflowContext->put('xml_filename', 'data/domain.xml');
-
-$config = new CodeGenerator\Config\ComponentList(
-    ...[
-        new CodeGenerator\Config\ComponentConfig(
-            CodeGenerator\Transformator\StringToFile::workflowComponentDescription(
-                Inspectio\WorkflowConfigFactory::SLOT_GRAPHML_XML,
-                'xml_filename'
-            )
+$workflowContext->put('part_one', 'Hello'); // init some data so it's available as an input slot name
+
+$config = new CodeGenerator\Config\Workflow(
+        // step one
+        new CodeGenerator\Workflow\ComponentDescriptionWithSlot(
+            function(string $inputHello) {
+                return $inputHello . ' World!';
+            },
+            'greeting', // output slot name
+            'part_one' // input slot name
+        ),
+        // step two
+        new CodeGenerator\Workflow\ComponentDescriptionWithInputSlotOnly(
+            function(string $greeting) {
+                echo $greeting;
+            },
+            'greeting', // input slot name
         ),
-    ]
 );
 
-$config->addConsoleCommands(new Inspectio\Console\XmlGenerateAllCommand());
-
 return $config;
 ```
+
+## Register additional Symfony CLI commands
+
+It is possible to add additional CLI commands to the code generator CLI. You can register additional Symfony CLI commands
+by adding them to the `$config` object via `$config->addConsoleCommands(new AwesomeCliCommand())`.
+
+## Register a monitor
+
+A monitor can be used to display code generation steps and progress.
+
+You can register a monitor instance of type `\OpenCodeModeling\CodeGenerator\Workflow\Monitoring\Monitoring` to the 
+`$config` object via `$config->setMonitor(new AwesomeMonitor())`. The Code Generator is shipped with a `Psr\Log\LoggerInterface`
+monitor (`\OpenCodeModeling\CodeGenerator\Workflow\Monitoring\LoggerMonitor`).
diff --git a/docs/book/03-cli.md b/docs/book/03-cli.md
index a300874..8967a23 100644
--- a/docs/book/03-cli.md
+++ b/docs/book/03-cli.md
@@ -1,3 +1,31 @@
 # Code Generator CLI
 
-TODO CLI call example
+The code generator CLI can be started through the `bin/ocmcg` executable. This prints the registered CLI commands from 
+workflows, and the default available CLI commands. The CLI command `ocmcg:workflow:run` executes the code generation 
+depending on the configuration file. You can use the CLI option `-c` to specify a specific configuration file.
+
+```
+===========================================
+Open Code Modeling - PHP Code Generator CLI
+===========================================
+
+
+Usage:
+  command [options] [arguments]
+
+Options:
+  -h, --help            Display this help message
+  -q, --quiet           Do not output any message
+  -V, --version         Display this application version
+      --ansi            Force ANSI output
+      --no-ansi         Disable ANSI output
+  -n, --no-interaction  Do not ask any interactive question
+  -c, --config=CONFIG   Configuration file
+  -v|vv|vvv, --verbose  Increase the verbosity of messages: 1 for normal output, 2 for more verbose output and 3 for debug
+
+Available commands:
+  help                Displays help for a command
+  list                Lists commands
+ ocmcg
+  ocmcg:workflow:run  Executes workflow from configuration file to generate code
+```
diff --git a/src/Config/ArrayConfig.php b/src/Config/ArrayConfig.php
deleted file mode 100644
index e4d05a5..0000000
--- a/src/Config/ArrayConfig.php
+++ /dev/null
@@ -1,57 +0,0 @@
-<?php
-
-/**
- * @see       https://github.com/open-code-modeling/php-code-generator for the canonical source repository
- * @copyright https://github.com/open-code-modeling/php-code-generator/blob/master/COPYRIGHT.md
- * @license   https://github.com/open-code-modeling/php-code-generator/blob/master/LICENSE.md MIT License
- */
-
-declare(strict_types=1);
-
-namespace OpenCodeModeling\CodeGenerator\Config;
-
-use OpenCodeModeling\CodeGenerator\Workflow\Description;
-use Symfony\Component\Console\Command\Command;
-
-/**
- * @deprecated Use \OpenCodeModeling\CodeGenerator\Config\Workflow
- */
-final class ArrayConfig implements Component
-{
-    /**
-     * @var Description[]
-     **/
-    private $config;
-
-    /**
-     * @var Command[]
-     **/
-    private $consoleCommands = [];
-
-    public function __construct(Description ...$config)
-    {
-        $this->config = $config;
-    }
-
-    public function componentDescriptions(): array
-    {
-        return $this->config;
-    }
-
-    public function consoleCommands(): iterable
-    {
-        return $this->consoleCommands;
-    }
-
-    /**
-     * Add console commands which are added to the code generator CLI
-     *
-     * @param Command ...$consoleCommands
-     */
-    public function addConsoleCommands(Command ...$consoleCommands): void
-    {
-        foreach ($consoleCommands as $consoleCommand) {
-            $this->consoleCommands[] = $consoleCommand;
-        }
-    }
-}
diff --git a/src/Config/ComponentCollection.php b/src/Config/ComponentCollection.php
deleted file mode 100644
index 8743543..0000000
--- a/src/Config/ComponentCollection.php
+++ /dev/null
@@ -1,18 +0,0 @@
-<?php
-
-/**
- * @see       https://github.com/open-code-modeling/php-code-generator for the canonical source repository
- * @copyright https://github.com/open-code-modeling/php-code-generator/blob/master/COPYRIGHT.md
- * @license   https://github.com/open-code-modeling/php-code-generator/blob/master/LICENSE.md MIT License
- */
-
-declare(strict_types=1);
-
-namespace OpenCodeModeling\CodeGenerator\Config;
-
-/**
- * @deprecated Use \OpenCodeModeling\CodeGenerator\Config\WorkflowCollection
- */
-interface ComponentCollection extends Config, \Iterator
-{
-}
diff --git a/src/Config/ComponentList.php b/src/Config/ComponentList.php
deleted file mode 100644
index 4cbab2a..0000000
--- a/src/Config/ComponentList.php
+++ /dev/null
@@ -1,80 +0,0 @@
-<?php
-
-/**
- * @see       https://github.com/open-code-modeling/php-code-generator for the canonical source repository
- * @copyright https://github.com/open-code-modeling/php-code-generator/blob/master/COPYRIGHT.md
- * @license   https://github.com/open-code-modeling/php-code-generator/blob/master/LICENSE.md MIT License
- */
-
-declare(strict_types=1);
-
-namespace OpenCodeModeling\CodeGenerator\Config;
-
-use Symfony\Component\Console\Command\Command;
-
-/**
- * @deprecated Use \OpenCodeModeling\CodeGenerator\Config\WorkflowList
- */
-final class ComponentList implements ComponentCollection
-{
-    private $position = 0;
-
-    /**
-     * @var Component[]
-     **/
-    private $components;
-
-    /**
-     * @var Command[]
-     **/
-    private $consoleCommands = [];
-
-    public function __construct(Component ...$components)
-    {
-        $this->components = $components;
-
-        foreach ($this->components as $component) {
-            $this->addConsoleCommands(...$component->consoleCommands());
-        }
-    }
-
-    public function rewind()
-    {
-        $this->position = 0;
-    }
-
-    public function current()
-    {
-        return $this->components[$this->position];
-    }
-
-    public function key()
-    {
-        return $this->position;
-    }
-
-    public function next()
-    {
-        ++$this->position;
-    }
-
-    public function valid()
-    {
-        return isset($this->components[$this->position]);
-    }
-
-    public function consoleCommands(): iterable
-    {
-        return $this->consoleCommands;
-    }
-
-    /**
-     * @param Command ...$consoleCommands
-     */
-    public function addConsoleCommands(Command ...$consoleCommands): void
-    {
-        foreach ($consoleCommands as $consoleCommand) {
-            $this->consoleCommands[] = $consoleCommand;
-        }
-    }
-}
diff --git a/src/Config/Config.php b/src/Config/Config.php
index 3bdbf5c..f54783d 100644
--- a/src/Config/Config.php
+++ b/src/Config/Config.php
@@ -10,6 +10,7 @@
 
 namespace OpenCodeModeling\CodeGenerator\Config;
 
+use OpenCodeModeling\CodeGenerator\Workflow\Monitoring\Monitoring;
 use Symfony\Component\Console\Command\Command;
 
 /**
@@ -23,4 +24,11 @@ interface Config
      * @return Command[]
      */
     public function consoleCommands(): iterable;
+
+    /**
+     * Monitoring instance for the workflow engine
+     *
+     * @return Monitoring|null
+     */
+    public function monitor(): ?Monitoring;
 }
diff --git a/src/Config/Workflow.php b/src/Config/Workflow.php
index 8bf4f64..f61b569 100644
--- a/src/Config/Workflow.php
+++ b/src/Config/Workflow.php
@@ -11,6 +11,7 @@
 namespace OpenCodeModeling\CodeGenerator\Config;
 
 use OpenCodeModeling\CodeGenerator\Workflow\Description;
+use OpenCodeModeling\CodeGenerator\Workflow\Monitoring\Monitoring;
 use Symfony\Component\Console\Command\Command;
 
 /**
@@ -28,6 +29,11 @@ final class Workflow implements WorkflowConfig
      **/
     private $consoleCommands = [];
 
+    /**
+     * @var Monitoring|null
+     */
+    private $monitor;
+
     public function __construct(Description ...$config)
     {
         $this->config = $config;
@@ -54,4 +60,14 @@ public function addConsoleCommands(Command ...$consoleCommands): void
             $this->consoleCommands[] = $consoleCommand;
         }
     }
+
+    public function monitor(): ?Monitoring
+    {
+        return $this->monitor;
+    }
+
+    public function setMonitor(Monitoring $monitor): void
+    {
+        $this->monitor = $monitor;
+    }
 }
diff --git a/src/Config/WorkflowList.php b/src/Config/WorkflowList.php
index 239feda..15773be 100644
--- a/src/Config/WorkflowList.php
+++ b/src/Config/WorkflowList.php
@@ -10,6 +10,7 @@
 
 namespace OpenCodeModeling\CodeGenerator\Config;
 
+use OpenCodeModeling\CodeGenerator\Workflow\Monitoring\Monitoring;
 use Symfony\Component\Console\Command\Command;
 
 /**
@@ -29,6 +30,11 @@ final class WorkflowList implements WorkflowCollection
      **/
     private $consoleCommands = [];
 
+    /**
+     * @var Monitoring|null
+     */
+    private $monitor;
+
     public function __construct(WorkflowConfig ...$components)
     {
         $this->components = $components;
@@ -77,4 +83,14 @@ public function addConsoleCommands(Command ...$consoleCommands): void
             $this->consoleCommands[] = $consoleCommand;
         }
     }
+
+    public function monitor(): ?Monitoring
+    {
+        return $this->monitor;
+    }
+
+    public function setMonitor(Monitoring $monitor): void
+    {
+        $this->monitor = $monitor;
+    }
 }
diff --git a/src/Console/WorkflowCommand.php b/src/Console/WorkflowCommand.php
index 860e4db..cbc354b 100644
--- a/src/Console/WorkflowCommand.php
+++ b/src/Console/WorkflowCommand.php
@@ -10,12 +10,11 @@
 
 namespace OpenCodeModeling\CodeGenerator\Console;
 
-use OpenCodeModeling\CodeGenerator\Config\Component;
-use OpenCodeModeling\CodeGenerator\Config\ComponentCollection;
-use OpenCodeModeling\CodeGenerator\Config\Config;
-use OpenCodeModeling\CodeGenerator\Config\WorkflowCollection;
-use OpenCodeModeling\CodeGenerator\Config\WorkflowConfig;
+use OpenCodeModeling\CodeGenerator\Config;
+use OpenCodeModeling\CodeGenerator\Console;
 use OpenCodeModeling\CodeGenerator\Exception\RuntimeException;
+use OpenCodeModeling\CodeGenerator\Workflow\Monitoring\Monitoring;
+use OpenCodeModeling\CodeGenerator\Workflow\Monitoring\NullMonitor;
 use OpenCodeModeling\CodeGenerator\Workflow\WorkflowContext;
 use OpenCodeModeling\CodeGenerator\Workflow\WorkflowEngine;
 use Symfony\Component\Console\Command\Command;
@@ -27,51 +26,51 @@
  */
 final class WorkflowCommand extends Command
 {
-    protected function configure()
+    protected function configure(): void
     {
         $this
             ->setName('ocmcg:workflow:run')
             ->setDescription('Executes workflow from configuration file to generate code');
     }
 
-    protected function execute(InputInterface $input, OutputInterface $output)
+    protected function execute(InputInterface $input, OutputInterface $output): int
     {
         /** @var WorkflowContext $workflowContext */
-        $workflowContext = $this->getHelper(\OpenCodeModeling\CodeGenerator\Console\WorkflowContext::class)->context();
+        $workflowContext = $this->getHelper(Console\WorkflowContext::class)->context();
 
         $config = $this->loadConfig($workflowContext);
 
-        if ($config instanceof WorkflowConfig
-            || $config instanceof Component
-        ) {
-            $this->executeWorkflow($config, $workflowContext);
-        } elseif ($config instanceof WorkflowCollection
-            || $config instanceof ComponentCollection
-        ) {
+        $monitor = $config->monitor();
+
+        if ($config instanceof Config\WorkflowConfig) {
+            $this->executeWorkflow($config, $workflowContext, $monitor);
+        } elseif ($config instanceof Config\WorkflowCollection) {
             foreach ($config as $workflowConfig) {
-                $this->executeWorkflow($workflowConfig, $workflowContext);
+                $this->executeWorkflow($workflowConfig, $workflowContext, $monitor);
             }
         } else {
             throw new RuntimeException(
-                \sprintf('$config must implement %s or %s', WorkflowConfig::class, WorkflowCollection::class)
+                \sprintf(
+                    '$config must implement %s or %s', Config\WorkflowConfig::class,
+                    Config\WorkflowCollection::class
+                )
             );
         }
 
         return 0;
     }
 
-    private function loadConfig(WorkflowContext $workflowContext): Config
+    private function loadConfig(WorkflowContext $workflowContext): Config\Config
     {
-        return $this->getHelper(\OpenCodeModeling\CodeGenerator\Console\Config::class)->resolver()->resolve($workflowContext);
+        return $this->getHelper(Console\Config::class)->resolver()->resolve($workflowContext);
     }
 
-    /**
-     * @param WorkflowConfig|Component $config
-     * @param WorkflowContext $workflowContext
-     */
-    private function executeWorkflow($config, WorkflowContext $workflowContext): void
-    {
-        $workflowEngine = new WorkflowEngine();
+    private function executeWorkflow(
+        Config\WorkflowConfig $config,
+        WorkflowContext $workflowContext,
+        ?Monitoring $monitor
+    ): void {
+        $workflowEngine = new WorkflowEngine($monitor ?: new NullMonitor());
         $workflowEngine->run($workflowContext, ...$config->componentDescriptions());
     }
 }
diff --git a/src/Workflow/Description.php b/src/Workflow/Description.php
index 9ccd3db..d80ab46 100644
--- a/src/Workflow/Description.php
+++ b/src/Workflow/Description.php
@@ -22,4 +22,11 @@ interface Description
      * @return callable
      */
     public function component(): callable;
+
+    /**
+     * A description of the component (purpose, what's generated, transformed, ...)
+     *
+     * @return string
+     */
+    public function description(): string;
 }
diff --git a/src/Workflow/DescriptionTrait.php b/src/Workflow/DescriptionTrait.php
index faadd18..3769a03 100644
--- a/src/Workflow/DescriptionTrait.php
+++ b/src/Workflow/DescriptionTrait.php
@@ -29,4 +29,9 @@ public function component(): callable
     {
         return $this->component;
     }
+
+    public function description(): string
+    {
+        return \is_object($this->component) ? \get_class($this->component) : static::class;
+    }
 }
diff --git a/src/Workflow/Monitoring/LoggerMonitor.php b/src/Workflow/Monitoring/LoggerMonitor.php
new file mode 100644
index 0000000..701a377
--- /dev/null
+++ b/src/Workflow/Monitoring/LoggerMonitor.php
@@ -0,0 +1,69 @@
+<?php
+
+/**
+ * @see       https://github.com/open-code-modeling/php-code-generator for the canonical source repository
+ * @copyright https://github.com/open-code-modeling/php-code-generator/blob/master/COPYRIGHT.md
+ * @license   https://github.com/open-code-modeling/php-code-generator/blob/master/LICENSE.md MIT License
+ */
+
+declare(strict_types=1);
+
+namespace OpenCodeModeling\CodeGenerator\Workflow\Monitoring;
+
+use OpenCodeModeling\CodeGenerator\Workflow\Description;
+use OpenCodeModeling\CodeGenerator\Workflow\DescriptionWithInputSlot;
+use OpenCodeModeling\CodeGenerator\Workflow\DescriptionWithOutputSlot;
+use Psr\Log\LoggerInterface;
+
+final class LoggerMonitor implements Monitoring
+{
+    /**
+     * @var LoggerInterface
+     **/
+    private $logger;
+
+    public function __construct(LoggerInterface $logger)
+    {
+        $this->logger = $logger;
+    }
+
+    public function start(Description $description): void
+    {
+        $this->logger->info('Start: ' . $description->description());
+    }
+
+    public function call(Description $description): void
+    {
+        if ($description instanceof DescriptionWithInputSlot) {
+            $this->logger->info(
+                \sprintf('|-- Executing with input slots "%s"', \implode('", "', $description->inputSlots())),
+            );
+        } else {
+            $this->logger->info('|-- Executing without input slots');
+        }
+    }
+
+    public function done(Description $description): void
+    {
+        if ($description instanceof DescriptionWithOutputSlot) {
+            $this->logger->info(\sprintf('|-- Done! Output stored in slot "%s"', $description->outputSlot()));
+        } else {
+            $this->logger->info('|-- Done!');
+        }
+    }
+
+    public function error(Description $description, \Throwable $e): void
+    {
+        $this->logger->error(
+            \sprintf('Error on "%s"! Reason: %s, File: %s, Line: %d',
+                $description->description(),
+                $e->getMessage(),
+                $e->getFile(),
+                $e->getLine()
+            ),
+            [
+                'exception' => $e,
+            ]
+        );
+    }
+}
diff --git a/src/Config/Component.php b/src/Workflow/Monitoring/Monitoring.php
similarity index 55%
rename from src/Config/Component.php
rename to src/Workflow/Monitoring/Monitoring.php
index 2eff068..9f36126 100644
--- a/src/Config/Component.php
+++ b/src/Workflow/Monitoring/Monitoring.php
@@ -8,19 +8,17 @@
 
 declare(strict_types=1);
 
-namespace OpenCodeModeling\CodeGenerator\Config;
+namespace OpenCodeModeling\CodeGenerator\Workflow\Monitoring;
 
 use OpenCodeModeling\CodeGenerator\Workflow\Description;
 
-/**
- * @deprecated Use \OpenCodeModeling\CodeGenerator\Config\WorkflowConfig
- */
-interface Component extends Config
+interface Monitoring
 {
-    /**
-     * Returns the component descriptions.
-     *
-     * @return Description[]
-     */
-    public function componentDescriptions(): array;
+    public function start(Description $description): void;
+
+    public function call(Description $description): void;
+
+    public function done(Description $description): void;
+
+    public function error(Description $description, \Throwable $e): void;
 }
diff --git a/src/Workflow/Monitoring/NullMonitor.php b/src/Workflow/Monitoring/NullMonitor.php
new file mode 100644
index 0000000..a3b6e8a
--- /dev/null
+++ b/src/Workflow/Monitoring/NullMonitor.php
@@ -0,0 +1,32 @@
+<?php
+
+/**
+ * @see       https://github.com/open-code-modeling/php-code-generator for the canonical source repository
+ * @copyright https://github.com/open-code-modeling/php-code-generator/blob/master/COPYRIGHT.md
+ * @license   https://github.com/open-code-modeling/php-code-generator/blob/master/LICENSE.md MIT License
+ */
+
+declare(strict_types=1);
+
+namespace OpenCodeModeling\CodeGenerator\Workflow\Monitoring;
+
+use OpenCodeModeling\CodeGenerator\Workflow\Description;
+
+final class NullMonitor implements Monitoring
+{
+    public function start(Description $description): void
+    {
+    }
+
+    public function call(Description $description): void
+    {
+    }
+
+    public function done(Description $description): void
+    {
+    }
+
+    public function error(Description $description, \Throwable $e): void
+    {
+    }
+}
diff --git a/src/Workflow/WorkflowEngine.php b/src/Workflow/WorkflowEngine.php
index 89a4189..ac18d15 100644
--- a/src/Workflow/WorkflowEngine.php
+++ b/src/Workflow/WorkflowEngine.php
@@ -10,12 +10,24 @@
 
 namespace OpenCodeModeling\CodeGenerator\Workflow;
 
+use OpenCodeModeling\CodeGenerator\Workflow\Monitoring\Monitoring;
+
 /**
  * The class `WorkflowEngine` processes a list of classes in a specified order which implement the `Description`
  * interface. The processing is started by the `run()` method.
  */
 final class WorkflowEngine
 {
+    /**
+     * @var Monitoring
+     **/
+    private $monitoring;
+
+    public function __construct(Monitoring $monitoring)
+    {
+        $this->monitoring = $monitoring;
+    }
+
     /**
      * Processes a list of classes in a specified order which implement the `Description`
      * interface.
@@ -26,16 +38,24 @@ final class WorkflowEngine
     public function run(WorkflowContext $context, Description ...$descriptions): void
     {
         foreach ($descriptions as $description) {
-            $component = $description->component();
+            try {
+                $this->monitoring->start($description);
+                $component = $description->component();
+                $this->monitoring->call($description);
 
-            if ($description instanceof DescriptionWithInputSlot) {
-                $result = $component(...$context->getByDescription($description));
-            } else {
-                $result = $component();
-            }
+                if ($description instanceof DescriptionWithInputSlot) {
+                    $result = $component(...$context->getByDescription($description));
+                } else {
+                    $result = $component();
+                }
 
-            if ($description instanceof DescriptionWithOutputSlot) {
-                $context->put($description->outputSlot(), $result);
+                if ($description instanceof DescriptionWithOutputSlot) {
+                    $context->put($description->outputSlot(), $result);
+                }
+                $this->monitoring->done($description);
+            } catch (\Throwable $e) {
+                $this->monitoring->error($description, $e);
+                break;
             }
         }
     }
diff --git a/tests/Workflow/WorkflowEngineTest.php b/tests/Workflow/WorkflowEngineTest.php
new file mode 100644
index 0000000..a5635bc
--- /dev/null
+++ b/tests/Workflow/WorkflowEngineTest.php
@@ -0,0 +1,49 @@
+<?php
+/*
+ * @see       https://github.com/open-code-modeling/php-code-generator for the canonical source repository
+ * @copyright https://github.com/open-code-modeling/php-code-generator/blob/master/COPYRIGHT.md
+ * @license   https://github.com/open-code-modeling/php-code-generator/blob/master/LICENSE.md MIT License
+ */
+
+declare(strict_types=1);
+
+namespace OpenCodeModelingTest\CodeGenerator\Workflow;
+
+use OpenCodeModeling\CodeGenerator\Workflow\ComponentDescriptionWithInputSlotOnly;
+use OpenCodeModeling\CodeGenerator\Workflow\Monitoring\Monitoring;
+use OpenCodeModeling\CodeGenerator\Workflow\WorkflowContext;
+use OpenCodeModeling\CodeGenerator\Workflow\WorkflowEngine;
+use PHPUnit\Framework\TestCase;
+use Prophecy\Argument;
+use Prophecy\PhpUnit\ProphecyTrait;
+
+final class WorkflowEngineTest extends TestCase
+{
+    use ProphecyTrait;
+
+    /**
+     * @test
+     */
+    public function it_executes_a_workflow(): void
+    {
+        $description = new ComponentDescriptionWithInputSlotOnly(
+            function(string $greeting) {
+                $this->assertSame('Hello Test!', $greeting);
+            },
+            'greeting',
+        );
+
+        $monitor = $this->prophesize(Monitoring::class);
+        $monitor->start(Argument::is($description))->shouldBeCalled();
+        $monitor->call(Argument::is($description))->shouldBeCalled();
+        $monitor->done(Argument::is($description))->shouldBeCalled();
+
+        $workflowContext = $this->prophesize(WorkflowContext::class);
+        $workflowContext->getByDescription(Argument::is($description))
+            ->willReturn(['Hello Test!'])
+            ->shouldBeCalled();
+
+        $workflowEngine = new WorkflowEngine($monitor->reveal());
+        $workflowEngine->run($workflowContext->reveal(), $description);
+    }
+}