diff --git a/.idea/php-test-framework.xml b/.idea/php-test-framework.xml
new file mode 100644
index 00000000..5aa0e33f
--- /dev/null
+++ b/.idea/php-test-framework.xml
@@ -0,0 +1,14 @@
+
+
+  
+    
+      
+        
+          
+            
+          
+        
+      
+    
+  
+
\ No newline at end of file
diff --git a/composer.json b/composer.json
index 81731254..0028702e 100644
--- a/composer.json
+++ b/composer.json
@@ -22,7 +22,8 @@
         "illuminate/contracts": ">=5.1",
         "illuminate/filesystem": ">=5.1",
         "illuminate/console": ">=5.1",
-        "php-libs/observable": "^1.0"
+        "php-libs/observable": "^1.3",
+        "php-libs/value-states": "^1.3"
     },
     "require-dev": {
         "fzaninotto/faker": "~1.4",
diff --git a/composer.lock b/composer.lock
index 6da42dcb..7ee45541 100644
--- a/composer.lock
+++ b/composer.lock
@@ -4,7 +4,7 @@
         "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies",
         "This file is @generated automatically"
     ],
-    "content-hash": "811e4566886ac6e2a5a71c710bd3c214",
+    "content-hash": "42ab9c20c5e6de27e9fab27a0869a8ef",
     "packages": [
         {
             "name": "composer/package-versions-deprecated",
@@ -81,40 +81,39 @@
         },
         {
             "name": "doctrine/cache",
-            "version": "1.10.2",
+            "version": "1.11.0",
             "source": {
                 "type": "git",
                 "url": "https://github.com/doctrine/cache.git",
-                "reference": "13e3381b25847283a91948d04640543941309727"
+                "reference": "a9c1b59eba5a08ca2770a76eddb88922f504e8e0"
             },
             "dist": {
                 "type": "zip",
-                "url": "https://api.github.com/repos/doctrine/cache/zipball/13e3381b25847283a91948d04640543941309727",
-                "reference": "13e3381b25847283a91948d04640543941309727",
+                "url": "https://api.github.com/repos/doctrine/cache/zipball/a9c1b59eba5a08ca2770a76eddb88922f504e8e0",
+                "reference": "a9c1b59eba5a08ca2770a76eddb88922f504e8e0",
                 "shasum": ""
             },
             "require": {
                 "php": "~7.1 || ^8.0"
             },
             "conflict": {
-                "doctrine/common": ">2.2,<2.4"
+                "doctrine/common": ">2.2,<2.4",
+                "psr/cache": ">=3"
             },
             "require-dev": {
                 "alcaeus/mongo-php-adapter": "^1.1",
-                "doctrine/coding-standard": "^6.0",
+                "cache/integration-tests": "dev-master",
+                "doctrine/coding-standard": "^8.0",
                 "mongodb/mongodb": "^1.1",
-                "phpunit/phpunit": "^7.0",
-                "predis/predis": "~1.0"
+                "phpunit/phpunit": "^7.0 || ^8.0 || ^9.0",
+                "predis/predis": "~1.0",
+                "psr/cache": "^1.0 || ^2.0",
+                "symfony/cache": "^4.4 || ^5.2"
             },
             "suggest": {
                 "alcaeus/mongo-php-adapter": "Required to use legacy MongoDB driver"
             },
             "type": "library",
-            "extra": {
-                "branch-alias": {
-                    "dev-master": "1.9.x-dev"
-                }
-            },
             "autoload": {
                 "psr-4": {
                     "Doctrine\\Common\\Cache\\": "lib/Doctrine/Common/Cache"
@@ -161,7 +160,7 @@
             ],
             "support": {
                 "issues": "https://github.com/doctrine/cache/issues",
-                "source": "https://github.com/doctrine/cache/tree/1.10.x"
+                "source": "https://github.com/doctrine/cache/tree/1.11.0"
             },
             "funding": [
                 {
@@ -177,37 +176,39 @@
                     "type": "tidelift"
                 }
             ],
-            "time": "2020-07-07T18:54:01+00:00"
+            "time": "2021-04-13T14:46:17+00:00"
         },
         {
             "name": "doctrine/dbal",
-            "version": "3.0.0",
+            "version": "3.1.0",
             "source": {
                 "type": "git",
                 "url": "https://github.com/doctrine/dbal.git",
-                "reference": "ee6d1260d5cc20ec506455a585945d7bdb98662c"
+                "reference": "5ba62e7e40df119424866064faf2cef66cb5232a"
             },
             "dist": {
                 "type": "zip",
-                "url": "https://api.github.com/repos/doctrine/dbal/zipball/ee6d1260d5cc20ec506455a585945d7bdb98662c",
-                "reference": "ee6d1260d5cc20ec506455a585945d7bdb98662c",
+                "url": "https://api.github.com/repos/doctrine/dbal/zipball/5ba62e7e40df119424866064faf2cef66cb5232a",
+                "reference": "5ba62e7e40df119424866064faf2cef66cb5232a",
                 "shasum": ""
             },
             "require": {
                 "composer/package-versions-deprecated": "^1.11.99",
                 "doctrine/cache": "^1.0",
+                "doctrine/deprecations": "^0.5.3",
                 "doctrine/event-manager": "^1.0",
                 "php": "^7.3 || ^8.0"
             },
             "require-dev": {
-                "doctrine/coding-standard": "^8.1",
-                "jetbrains/phpstorm-stubs": "^2019.1",
-                "phpstan/phpstan": "^0.12.40",
+                "doctrine/coding-standard": "8.2.0",
+                "jetbrains/phpstorm-stubs": "2020.2",
+                "phpstan/phpstan": "0.12.81",
                 "phpstan/phpstan-strict-rules": "^0.12.2",
-                "phpunit/phpunit": "^9.4",
-                "psalm/plugin-phpunit": "^0.10.0",
+                "phpunit/phpunit": "9.5.0",
+                "psalm/plugin-phpunit": "0.13.0",
+                "squizlabs/php_codesniffer": "3.6.0",
                 "symfony/console": "^2.0.5|^3.0|^4.0|^5.0",
-                "vimeo/psalm": "^3.17.2"
+                "vimeo/psalm": "4.6.4"
             },
             "suggest": {
                 "symfony/console": "For helpful console commands such as SQL execution and import of files."
@@ -216,11 +217,6 @@
                 "bin/doctrine-dbal"
             ],
             "type": "library",
-            "extra": {
-                "branch-alias": {
-                    "dev-master": "4.0.x-dev"
-                }
-            },
             "autoload": {
                 "psr-4": {
                     "Doctrine\\DBAL\\": "src"
@@ -272,7 +268,7 @@
             ],
             "support": {
                 "issues": "https://github.com/doctrine/dbal/issues",
-                "source": "https://github.com/doctrine/dbal/tree/3.0.0"
+                "source": "https://github.com/doctrine/dbal/tree/3.1.0"
             },
             "funding": [
                 {
@@ -288,7 +284,50 @@
                     "type": "tidelift"
                 }
             ],
-            "time": "2020-11-15T18:20:41+00:00"
+            "time": "2021-04-19T17:51:23+00:00"
+        },
+        {
+            "name": "doctrine/deprecations",
+            "version": "v0.5.3",
+            "source": {
+                "type": "git",
+                "url": "https://github.com/doctrine/deprecations.git",
+                "reference": "9504165960a1f83cc1480e2be1dd0a0478561314"
+            },
+            "dist": {
+                "type": "zip",
+                "url": "https://api.github.com/repos/doctrine/deprecations/zipball/9504165960a1f83cc1480e2be1dd0a0478561314",
+                "reference": "9504165960a1f83cc1480e2be1dd0a0478561314",
+                "shasum": ""
+            },
+            "require": {
+                "php": "^7.1|^8.0"
+            },
+            "require-dev": {
+                "doctrine/coding-standard": "^6.0|^7.0|^8.0",
+                "phpunit/phpunit": "^7.0|^8.0|^9.0",
+                "psr/log": "^1.0"
+            },
+            "suggest": {
+                "psr/log": "Allows logging deprecations via PSR-3 logger implementation"
+            },
+            "type": "library",
+            "autoload": {
+                "psr-4": {
+                    "Doctrine\\Deprecations\\": "lib/Doctrine/Deprecations"
+                }
+            },
+            "notification-url": "https://packagist.org/downloads/",
+            "license": [
+                "MIT"
+            ],
+            "description": "A small layer on top of trigger_error(E_USER_DEPRECATED) or PSR-3 logging with options to disable all deprecations or selectively for packages.",
+            "homepage": "https://www.doctrine-project.org/",
+            "support": {
+                "issues": "https://github.com/doctrine/deprecations/issues",
+                "source": "https://github.com/doctrine/deprecations/tree/v0.5.3"
+            },
+            "time": "2021-03-21T12:59:47+00:00"
         },
         {
             "name": "doctrine/event-manager",
@@ -481,16 +520,16 @@
         },
         {
             "name": "illuminate/collections",
-            "version": "v8.34.0",
+            "version": "v8.41.0",
             "source": {
                 "type": "git",
                 "url": "https://github.com/illuminate/collections.git",
-                "reference": "e18d6e4cf03dd597bc3ecd86fefc2023d0c7a5e8"
+                "reference": "deccb956d38710f3f8baf36dd876c3fa1585ec22"
             },
             "dist": {
                 "type": "zip",
-                "url": "https://api.github.com/repos/illuminate/collections/zipball/e18d6e4cf03dd597bc3ecd86fefc2023d0c7a5e8",
-                "reference": "e18d6e4cf03dd597bc3ecd86fefc2023d0c7a5e8",
+                "url": "https://api.github.com/repos/illuminate/collections/zipball/deccb956d38710f3f8baf36dd876c3fa1585ec22",
+                "reference": "deccb956d38710f3f8baf36dd876c3fa1585ec22",
                 "shasum": ""
             },
             "require": {
@@ -531,20 +570,20 @@
                 "issues": "https://github.com/laravel/framework/issues",
                 "source": "https://github.com/laravel/framework"
             },
-            "time": "2021-03-19T00:05:33+00:00"
+            "time": "2021-04-22T21:08:09+00:00"
         },
         {
             "name": "illuminate/console",
-            "version": "v8.34.0",
+            "version": "v8.41.0",
             "source": {
                 "type": "git",
                 "url": "https://github.com/illuminate/console.git",
-                "reference": "dcc1fd330b7ea8fcf259bbf73243bfedc98e45a3"
+                "reference": "395002ac2d4ec404c42e6e97997f4236dc8ab2b6"
             },
             "dist": {
                 "type": "zip",
-                "url": "https://api.github.com/repos/illuminate/console/zipball/dcc1fd330b7ea8fcf259bbf73243bfedc98e45a3",
-                "reference": "dcc1fd330b7ea8fcf259bbf73243bfedc98e45a3",
+                "url": "https://api.github.com/repos/illuminate/console/zipball/395002ac2d4ec404c42e6e97997f4236dc8ab2b6",
+                "reference": "395002ac2d4ec404c42e6e97997f4236dc8ab2b6",
                 "shasum": ""
             },
             "require": {
@@ -591,11 +630,11 @@
                 "issues": "https://github.com/laravel/framework/issues",
                 "source": "https://github.com/laravel/framework"
             },
-            "time": "2021-03-16T21:53:44+00:00"
+            "time": "2021-04-26T12:39:58+00:00"
         },
         {
             "name": "illuminate/container",
-            "version": "v8.34.0",
+            "version": "v8.41.0",
             "source": {
                 "type": "git",
                 "url": "https://github.com/illuminate/container.git",
@@ -646,16 +685,16 @@
         },
         {
             "name": "illuminate/contracts",
-            "version": "v8.34.0",
+            "version": "v8.41.0",
             "source": {
                 "type": "git",
                 "url": "https://github.com/illuminate/contracts.git",
-                "reference": "121cea1d8b8772bc7fee99c71ecf0f57c1d77b3b"
+                "reference": "64abbe2aeee0855a11cfce49d0ea08a0aa967cd2"
             },
             "dist": {
                 "type": "zip",
-                "url": "https://api.github.com/repos/illuminate/contracts/zipball/121cea1d8b8772bc7fee99c71ecf0f57c1d77b3b",
-                "reference": "121cea1d8b8772bc7fee99c71ecf0f57c1d77b3b",
+                "url": "https://api.github.com/repos/illuminate/contracts/zipball/64abbe2aeee0855a11cfce49d0ea08a0aa967cd2",
+                "reference": "64abbe2aeee0855a11cfce49d0ea08a0aa967cd2",
                 "shasum": ""
             },
             "require": {
@@ -690,20 +729,20 @@
                 "issues": "https://github.com/laravel/framework/issues",
                 "source": "https://github.com/laravel/framework"
             },
-            "time": "2021-03-12T14:45:30+00:00"
+            "time": "2021-05-06T14:58:48+00:00"
         },
         {
             "name": "illuminate/database",
-            "version": "v8.34.0",
+            "version": "v8.41.0",
             "source": {
                 "type": "git",
                 "url": "https://github.com/illuminate/database.git",
-                "reference": "74a165fd07b36cc0ea3558fa391b762867af87e8"
+                "reference": "6277b39728bce436d2509d215223137d87265792"
             },
             "dist": {
                 "type": "zip",
-                "url": "https://api.github.com/repos/illuminate/database/zipball/74a165fd07b36cc0ea3558fa391b762867af87e8",
-                "reference": "74a165fd07b36cc0ea3558fa391b762867af87e8",
+                "url": "https://api.github.com/repos/illuminate/database/zipball/6277b39728bce436d2509d215223137d87265792",
+                "reference": "6277b39728bce436d2509d215223137d87265792",
                 "shasum": ""
             },
             "require": {
@@ -758,20 +797,20 @@
                 "issues": "https://github.com/laravel/framework/issues",
                 "source": "https://github.com/laravel/framework"
             },
-            "time": "2021-03-23T15:12:51+00:00"
+            "time": "2021-05-11T13:24:37+00:00"
         },
         {
             "name": "illuminate/filesystem",
-            "version": "v8.34.0",
+            "version": "v8.41.0",
             "source": {
                 "type": "git",
                 "url": "https://github.com/illuminate/filesystem.git",
-                "reference": "25e31d4f114baf1f99110bb5fc7538f26b4367cb"
+                "reference": "8ef5902052c5b3bb4a6c1c3afc399f30e7723cb8"
             },
             "dist": {
                 "type": "zip",
-                "url": "https://api.github.com/repos/illuminate/filesystem/zipball/25e31d4f114baf1f99110bb5fc7538f26b4367cb",
-                "reference": "25e31d4f114baf1f99110bb5fc7538f26b4367cb",
+                "url": "https://api.github.com/repos/illuminate/filesystem/zipball/8ef5902052c5b3bb4a6c1c3afc399f30e7723cb8",
+                "reference": "8ef5902052c5b3bb4a6c1c3afc399f30e7723cb8",
                 "shasum": ""
             },
             "require": {
@@ -820,11 +859,11 @@
                 "issues": "https://github.com/laravel/framework/issues",
                 "source": "https://github.com/laravel/framework"
             },
-            "time": "2021-03-21T13:46:59+00:00"
+            "time": "2021-04-05T18:45:36+00:00"
         },
         {
             "name": "illuminate/macroable",
-            "version": "v8.34.0",
+            "version": "v8.41.0",
             "source": {
                 "type": "git",
                 "url": "https://github.com/illuminate/macroable.git",
@@ -870,16 +909,16 @@
         },
         {
             "name": "illuminate/support",
-            "version": "v8.34.0",
+            "version": "v8.41.0",
             "source": {
                 "type": "git",
                 "url": "https://github.com/illuminate/support.git",
-                "reference": "b7b27e758b68aad44558c62e7374328835895386"
+                "reference": "31e91a12f0aac770d02a05b5d5771829132213b4"
             },
             "dist": {
                 "type": "zip",
-                "url": "https://api.github.com/repos/illuminate/support/zipball/b7b27e758b68aad44558c62e7374328835895386",
-                "reference": "b7b27e758b68aad44558c62e7374328835895386",
+                "url": "https://api.github.com/repos/illuminate/support/zipball/31e91a12f0aac770d02a05b5d5771829132213b4",
+                "reference": "31e91a12f0aac770d02a05b5d5771829132213b4",
                 "shasum": ""
             },
             "require": {
@@ -934,20 +973,20 @@
                 "issues": "https://github.com/laravel/framework/issues",
                 "source": "https://github.com/laravel/framework"
             },
-            "time": "2021-03-21T13:37:37+00:00"
+            "time": "2021-05-10T13:42:57+00:00"
         },
         {
             "name": "nesbot/carbon",
-            "version": "2.46.0",
+            "version": "2.48.0",
             "source": {
                 "type": "git",
                 "url": "https://github.com/briannesbitt/Carbon.git",
-                "reference": "2fd2c4a77d58a4e95234c8a61c5df1f157a91bf4"
+                "reference": "d3c447f21072766cddec3522f9468a5849a76147"
             },
             "dist": {
                 "type": "zip",
-                "url": "https://api.github.com/repos/briannesbitt/Carbon/zipball/2fd2c4a77d58a4e95234c8a61c5df1f157a91bf4",
-                "reference": "2fd2c4a77d58a4e95234c8a61c5df1f157a91bf4",
+                "url": "https://api.github.com/repos/briannesbitt/Carbon/zipball/d3c447f21072766cddec3522f9468a5849a76147",
+                "reference": "d3c447f21072766cddec3522f9468a5849a76147",
                 "shasum": ""
             },
             "require": {
@@ -1027,20 +1066,20 @@
                     "type": "tidelift"
                 }
             ],
-            "time": "2021-02-24T17:30:44+00:00"
+            "time": "2021-05-07T10:08:30+00:00"
         },
         {
             "name": "php-libs/observable",
-            "version": "1.0",
+            "version": "1.3",
             "source": {
                 "type": "git",
                 "url": "https://github.com/php-libs/observable.git",
-                "reference": "68efabfa33f9bbe1e25e1b7c95e6bcd7fc5b62c5"
+                "reference": "0f6b33db27228c7800556a2c11e1ca7a7c80871e"
             },
             "dist": {
                 "type": "zip",
-                "url": "https://api.github.com/repos/php-libs/observable/zipball/68efabfa33f9bbe1e25e1b7c95e6bcd7fc5b62c5",
-                "reference": "68efabfa33f9bbe1e25e1b7c95e6bcd7fc5b62c5",
+                "url": "https://api.github.com/repos/php-libs/observable/zipball/0f6b33db27228c7800556a2c11e1ca7a7c80871e",
+                "reference": "0f6b33db27228c7800556a2c11e1ca7a7c80871e",
                 "shasum": ""
             },
             "require": {
@@ -1059,9 +1098,44 @@
             "description": "Provides clases to simplify observer pattern implementation",
             "support": {
                 "issues": "https://github.com/php-libs/observable/issues",
-                "source": "https://github.com/php-libs/observable/tree/1.0"
+                "source": "https://github.com/php-libs/observable/tree/1.3"
+            },
+            "time": "2021-05-19T07:15:17+00:00"
+        },
+        {
+            "name": "php-libs/value-states",
+            "version": "1.3",
+            "source": {
+                "type": "git",
+                "url": "https://github.com/php-libs/value-state.git",
+                "reference": "d9dcc44c5826dfbd7e7350bf5b217654151bf773"
+            },
+            "dist": {
+                "type": "zip",
+                "url": "https://api.github.com/repos/php-libs/value-state/zipball/d9dcc44c5826dfbd7e7350bf5b217654151bf773",
+                "reference": "d9dcc44c5826dfbd7e7350bf5b217654151bf773",
+                "shasum": ""
+            },
+            "require": {
+                "php": "^8.0",
+                "php-libs/observable": "^1.3"
+            },
+            "type": "library",
+            "autoload": {
+                "psr-4": {
+                    "PhpLibs\\ValueState\\": "src/PhpLibs/ValueState"
+                }
+            },
+            "notification-url": "https://packagist.org/downloads/",
+            "license": [
+                "MIT"
+            ],
+            "description": "Extends php-libs/observer to track if a values have been initialized or modified (changed after initialization)",
+            "support": {
+                "issues": "https://github.com/php-libs/value-state/issues",
+                "source": "https://github.com/php-libs/value-state/tree/1.3"
             },
-            "time": "2021-05-06T20:42:12+00:00"
+            "time": "2021-05-19T07:16:58+00:00"
         },
         {
             "name": "psr/container",
@@ -1164,16 +1238,16 @@
         },
         {
             "name": "symfony/console",
-            "version": "v5.2.5",
+            "version": "v5.2.8",
             "source": {
                 "type": "git",
                 "url": "https://github.com/symfony/console.git",
-                "reference": "938ebbadae1b0a9c9d1ec313f87f9708609f1b79"
+                "reference": "864568fdc0208b3eba3638b6000b69d2386e6768"
             },
             "dist": {
                 "type": "zip",
-                "url": "https://api.github.com/repos/symfony/console/zipball/938ebbadae1b0a9c9d1ec313f87f9708609f1b79",
-                "reference": "938ebbadae1b0a9c9d1ec313f87f9708609f1b79",
+                "url": "https://api.github.com/repos/symfony/console/zipball/864568fdc0208b3eba3638b6000b69d2386e6768",
+                "reference": "864568fdc0208b3eba3638b6000b69d2386e6768",
                 "shasum": ""
             },
             "require": {
@@ -1241,7 +1315,7 @@
                 "terminal"
             ],
             "support": {
-                "source": "https://github.com/symfony/console/tree/v5.2.5"
+                "source": "https://github.com/symfony/console/tree/v5.2.8"
             },
             "funding": [
                 {
@@ -1257,20 +1331,20 @@
                     "type": "tidelift"
                 }
             ],
-            "time": "2021-03-06T13:42:15+00:00"
+            "time": "2021-05-11T15:45:21+00:00"
         },
         {
             "name": "symfony/finder",
-            "version": "v5.2.4",
+            "version": "v5.2.8",
             "source": {
                 "type": "git",
                 "url": "https://github.com/symfony/finder.git",
-                "reference": "0d639a0943822626290d169965804f79400e6a04"
+                "reference": "eccb8be70d7a6a2230d05f6ecede40f3fdd9e252"
             },
             "dist": {
                 "type": "zip",
-                "url": "https://api.github.com/repos/symfony/finder/zipball/0d639a0943822626290d169965804f79400e6a04",
-                "reference": "0d639a0943822626290d169965804f79400e6a04",
+                "url": "https://api.github.com/repos/symfony/finder/zipball/eccb8be70d7a6a2230d05f6ecede40f3fdd9e252",
+                "reference": "eccb8be70d7a6a2230d05f6ecede40f3fdd9e252",
                 "shasum": ""
             },
             "require": {
@@ -1302,7 +1376,7 @@
             "description": "Finds files and directories via an intuitive fluent interface",
             "homepage": "https://symfony.com",
             "support": {
-                "source": "https://github.com/symfony/finder/tree/v5.2.4"
+                "source": "https://github.com/symfony/finder/tree/v5.2.8"
             },
             "funding": [
                 {
@@ -1318,7 +1392,7 @@
                     "type": "tidelift"
                 }
             ],
-            "time": "2021-02-15T18:55:04+00:00"
+            "time": "2021-05-10T14:39:23+00:00"
         },
         {
             "name": "symfony/polyfill-ctype",
@@ -1808,16 +1882,16 @@
         },
         {
             "name": "symfony/process",
-            "version": "v5.2.4",
+            "version": "v5.2.7",
             "source": {
                 "type": "git",
                 "url": "https://github.com/symfony/process.git",
-                "reference": "313a38f09c77fbcdc1d223e57d368cea76a2fd2f"
+                "reference": "98cb8eeb72e55d4196dd1e36f1f16e7b3a9a088e"
             },
             "dist": {
                 "type": "zip",
-                "url": "https://api.github.com/repos/symfony/process/zipball/313a38f09c77fbcdc1d223e57d368cea76a2fd2f",
-                "reference": "313a38f09c77fbcdc1d223e57d368cea76a2fd2f",
+                "url": "https://api.github.com/repos/symfony/process/zipball/98cb8eeb72e55d4196dd1e36f1f16e7b3a9a088e",
+                "reference": "98cb8eeb72e55d4196dd1e36f1f16e7b3a9a088e",
                 "shasum": ""
             },
             "require": {
@@ -1850,7 +1924,7 @@
             "description": "Executes commands in sub-processes",
             "homepage": "https://symfony.com",
             "support": {
-                "source": "https://github.com/symfony/process/tree/v5.2.4"
+                "source": "https://github.com/symfony/process/tree/v5.3.0-BETA1"
             },
             "funding": [
                 {
@@ -1866,25 +1940,25 @@
                     "type": "tidelift"
                 }
             ],
-            "time": "2021-01-27T10:15:41+00:00"
+            "time": "2021-04-08T10:27:02+00:00"
         },
         {
             "name": "symfony/service-contracts",
-            "version": "v2.2.0",
+            "version": "v2.4.0",
             "source": {
                 "type": "git",
                 "url": "https://github.com/symfony/service-contracts.git",
-                "reference": "d15da7ba4957ffb8f1747218be9e1a121fd298a1"
+                "reference": "f040a30e04b57fbcc9c6cbcf4dbaa96bd318b9bb"
             },
             "dist": {
                 "type": "zip",
-                "url": "https://api.github.com/repos/symfony/service-contracts/zipball/d15da7ba4957ffb8f1747218be9e1a121fd298a1",
-                "reference": "d15da7ba4957ffb8f1747218be9e1a121fd298a1",
+                "url": "https://api.github.com/repos/symfony/service-contracts/zipball/f040a30e04b57fbcc9c6cbcf4dbaa96bd318b9bb",
+                "reference": "f040a30e04b57fbcc9c6cbcf4dbaa96bd318b9bb",
                 "shasum": ""
             },
             "require": {
                 "php": ">=7.2.5",
-                "psr/container": "^1.0"
+                "psr/container": "^1.1"
             },
             "suggest": {
                 "symfony/service-implementation": ""
@@ -1892,7 +1966,7 @@
             "type": "library",
             "extra": {
                 "branch-alias": {
-                    "dev-master": "2.2-dev"
+                    "dev-main": "2.4-dev"
                 },
                 "thanks": {
                     "name": "symfony/contracts",
@@ -1929,7 +2003,7 @@
                 "standards"
             ],
             "support": {
-                "source": "https://github.com/symfony/service-contracts/tree/master"
+                "source": "https://github.com/symfony/service-contracts/tree/v2.4.0"
             },
             "funding": [
                 {
@@ -1945,20 +2019,20 @@
                     "type": "tidelift"
                 }
             ],
-            "time": "2020-09-07T11:33:47+00:00"
+            "time": "2021-04-01T10:43:52+00:00"
         },
         {
             "name": "symfony/string",
-            "version": "v5.2.4",
+            "version": "v5.2.8",
             "source": {
                 "type": "git",
                 "url": "https://github.com/symfony/string.git",
-                "reference": "4e78d7d47061fa183639927ec40d607973699609"
+                "reference": "01b35eb64cac8467c3f94cd0ce2d0d376bb7d1db"
             },
             "dist": {
                 "type": "zip",
-                "url": "https://api.github.com/repos/symfony/string/zipball/4e78d7d47061fa183639927ec40d607973699609",
-                "reference": "4e78d7d47061fa183639927ec40d607973699609",
+                "url": "https://api.github.com/repos/symfony/string/zipball/01b35eb64cac8467c3f94cd0ce2d0d376bb7d1db",
+                "reference": "01b35eb64cac8467c3f94cd0ce2d0d376bb7d1db",
                 "shasum": ""
             },
             "require": {
@@ -2012,7 +2086,7 @@
                 "utf8"
             ],
             "support": {
-                "source": "https://github.com/symfony/string/tree/v5.2.4"
+                "source": "https://github.com/symfony/string/tree/v5.2.8"
             },
             "funding": [
                 {
@@ -2028,20 +2102,20 @@
                     "type": "tidelift"
                 }
             ],
-            "time": "2021-02-16T10:20:28+00:00"
+            "time": "2021-05-10T14:56:10+00:00"
         },
         {
             "name": "symfony/translation",
-            "version": "v5.2.5",
+            "version": "v5.2.8",
             "source": {
                 "type": "git",
                 "url": "https://github.com/symfony/translation.git",
-                "reference": "0947ab1e3aabd22a6bef393874b2555d2bb976da"
+                "reference": "445caa74a5986f1cc9dd91a2975ef68fa7cb2068"
             },
             "dist": {
                 "type": "zip",
-                "url": "https://api.github.com/repos/symfony/translation/zipball/0947ab1e3aabd22a6bef393874b2555d2bb976da",
-                "reference": "0947ab1e3aabd22a6bef393874b2555d2bb976da",
+                "url": "https://api.github.com/repos/symfony/translation/zipball/445caa74a5986f1cc9dd91a2975ef68fa7cb2068",
+                "reference": "445caa74a5986f1cc9dd91a2975ef68fa7cb2068",
                 "shasum": ""
             },
             "require": {
@@ -2105,7 +2179,7 @@
             "description": "Provides tools to internationalize your application",
             "homepage": "https://symfony.com",
             "support": {
-                "source": "https://github.com/symfony/translation/tree/v5.2.5"
+                "source": "https://github.com/symfony/translation/tree/v5.2.8"
             },
             "funding": [
                 {
@@ -2121,20 +2195,20 @@
                     "type": "tidelift"
                 }
             ],
-            "time": "2021-03-06T07:59:01+00:00"
+            "time": "2021-05-07T13:41:16+00:00"
         },
         {
             "name": "symfony/translation-contracts",
-            "version": "v2.3.0",
+            "version": "v2.4.0",
             "source": {
                 "type": "git",
                 "url": "https://github.com/symfony/translation-contracts.git",
-                "reference": "e2eaa60b558f26a4b0354e1bbb25636efaaad105"
+                "reference": "95c812666f3e91db75385749fe219c5e494c7f95"
             },
             "dist": {
                 "type": "zip",
-                "url": "https://api.github.com/repos/symfony/translation-contracts/zipball/e2eaa60b558f26a4b0354e1bbb25636efaaad105",
-                "reference": "e2eaa60b558f26a4b0354e1bbb25636efaaad105",
+                "url": "https://api.github.com/repos/symfony/translation-contracts/zipball/95c812666f3e91db75385749fe219c5e494c7f95",
+                "reference": "95c812666f3e91db75385749fe219c5e494c7f95",
                 "shasum": ""
             },
             "require": {
@@ -2146,7 +2220,7 @@
             "type": "library",
             "extra": {
                 "branch-alias": {
-                    "dev-master": "2.3-dev"
+                    "dev-main": "2.4-dev"
                 },
                 "thanks": {
                     "name": "symfony/contracts",
@@ -2183,7 +2257,7 @@
                 "standards"
             ],
             "support": {
-                "source": "https://github.com/symfony/translation-contracts/tree/v2.3.0"
+                "source": "https://github.com/symfony/translation-contracts/tree/v2.4.0"
             },
             "funding": [
                 {
@@ -2199,7 +2273,7 @@
                     "type": "tidelift"
                 }
             ],
-            "time": "2020-09-28T13:05:58+00:00"
+            "time": "2021-03-23T23:28:01+00:00"
         },
         {
             "name": "voku/portable-ascii",
@@ -2783,16 +2857,16 @@
         },
         {
             "name": "nikic/php-parser",
-            "version": "v4.10.4",
+            "version": "v4.10.5",
             "source": {
                 "type": "git",
                 "url": "https://github.com/nikic/PHP-Parser.git",
-                "reference": "c6d052fc58cb876152f89f532b95a8d7907e7f0e"
+                "reference": "4432ba399e47c66624bc73c8c0f811e5c109576f"
             },
             "dist": {
                 "type": "zip",
-                "url": "https://api.github.com/repos/nikic/PHP-Parser/zipball/c6d052fc58cb876152f89f532b95a8d7907e7f0e",
-                "reference": "c6d052fc58cb876152f89f532b95a8d7907e7f0e",
+                "url": "https://api.github.com/repos/nikic/PHP-Parser/zipball/4432ba399e47c66624bc73c8c0f811e5c109576f",
+                "reference": "4432ba399e47c66624bc73c8c0f811e5c109576f",
                 "shasum": ""
             },
             "require": {
@@ -2833,9 +2907,9 @@
             ],
             "support": {
                 "issues": "https://github.com/nikic/PHP-Parser/issues",
-                "source": "https://github.com/nikic/PHP-Parser/tree/v4.10.4"
+                "source": "https://github.com/nikic/PHP-Parser/tree/v4.10.5"
             },
-            "time": "2020-12-20T10:01:03+00:00"
+            "time": "2021-05-03T19:11:20+00:00"
         },
         {
             "name": "phar-io/manifest",
@@ -3175,16 +3249,16 @@
         },
         {
             "name": "phpunit/php-code-coverage",
-            "version": "9.2.5",
+            "version": "9.2.6",
             "source": {
                 "type": "git",
                 "url": "https://github.com/sebastianbergmann/php-code-coverage.git",
-                "reference": "f3e026641cc91909d421802dd3ac7827ebfd97e1"
+                "reference": "f6293e1b30a2354e8428e004689671b83871edde"
             },
             "dist": {
                 "type": "zip",
-                "url": "https://api.github.com/repos/sebastianbergmann/php-code-coverage/zipball/f3e026641cc91909d421802dd3ac7827ebfd97e1",
-                "reference": "f3e026641cc91909d421802dd3ac7827ebfd97e1",
+                "url": "https://api.github.com/repos/sebastianbergmann/php-code-coverage/zipball/f6293e1b30a2354e8428e004689671b83871edde",
+                "reference": "f6293e1b30a2354e8428e004689671b83871edde",
                 "shasum": ""
             },
             "require": {
@@ -3240,7 +3314,7 @@
             ],
             "support": {
                 "issues": "https://github.com/sebastianbergmann/php-code-coverage/issues",
-                "source": "https://github.com/sebastianbergmann/php-code-coverage/tree/9.2.5"
+                "source": "https://github.com/sebastianbergmann/php-code-coverage/tree/9.2.6"
             },
             "funding": [
                 {
@@ -3248,7 +3322,7 @@
                     "type": "github"
                 }
             ],
-            "time": "2020-11-28T06:44:49+00:00"
+            "time": "2021-03-28T07:26:59+00:00"
         },
         {
             "name": "phpunit/php-file-iterator",
@@ -4610,16 +4684,16 @@
         },
         {
             "name": "symfony/config",
-            "version": "v5.2.7",
+            "version": "v5.2.8",
             "source": {
                 "type": "git",
                 "url": "https://github.com/symfony/config.git",
-                "reference": "3817662ada105c8c4d1afdb4ec003003efd1d8d8"
+                "reference": "8dfa5f8adea9cd5155920069224beb04f11d6b7e"
             },
             "dist": {
                 "type": "zip",
-                "url": "https://api.github.com/repos/symfony/config/zipball/3817662ada105c8c4d1afdb4ec003003efd1d8d8",
-                "reference": "3817662ada105c8c4d1afdb4ec003003efd1d8d8",
+                "url": "https://api.github.com/repos/symfony/config/zipball/8dfa5f8adea9cd5155920069224beb04f11d6b7e",
+                "reference": "8dfa5f8adea9cd5155920069224beb04f11d6b7e",
                 "shasum": ""
             },
             "require": {
@@ -4668,7 +4742,7 @@
             "description": "Helps you find, load, combine, autofill and validate configuration values of any kind",
             "homepage": "https://symfony.com",
             "support": {
-                "source": "https://github.com/symfony/config/tree/v5.2.7"
+                "source": "https://github.com/symfony/config/tree/v5.2.8"
             },
             "funding": [
                 {
@@ -4684,20 +4758,20 @@
                     "type": "tidelift"
                 }
             ],
-            "time": "2021-04-07T16:07:52+00:00"
+            "time": "2021-05-07T13:41:16+00:00"
         },
         {
             "name": "symfony/dependency-injection",
-            "version": "v5.2.7",
+            "version": "v5.2.8",
             "source": {
                 "type": "git",
                 "url": "https://github.com/symfony/dependency-injection.git",
-                "reference": "6ca378b99e3c9ba6127eb43b68389fb2b7348577"
+                "reference": "024e929da5a994cbab0ce2291d332f7edf926acf"
             },
             "dist": {
                 "type": "zip",
-                "url": "https://api.github.com/repos/symfony/dependency-injection/zipball/6ca378b99e3c9ba6127eb43b68389fb2b7348577",
-                "reference": "6ca378b99e3c9ba6127eb43b68389fb2b7348577",
+                "url": "https://api.github.com/repos/symfony/dependency-injection/zipball/024e929da5a994cbab0ce2291d332f7edf926acf",
+                "reference": "024e929da5a994cbab0ce2291d332f7edf926acf",
                 "shasum": ""
             },
             "require": {
@@ -4755,7 +4829,7 @@
             "description": "Allows you to standardize and centralize the way objects are constructed in your application",
             "homepage": "https://symfony.com",
             "support": {
-                "source": "https://github.com/symfony/dependency-injection/tree/v5.2.7"
+                "source": "https://github.com/symfony/dependency-injection/tree/v5.2.8"
             },
             "funding": [
                 {
@@ -4771,7 +4845,7 @@
                     "type": "tidelift"
                 }
             ],
-            "time": "2021-04-24T14:32:26+00:00"
+            "time": "2021-05-11T16:07:35+00:00"
         },
         {
             "name": "symfony/deprecation-contracts",
diff --git a/config/reliese.php b/config/reliese.php
index 4a45859c..f7bcf0de 100644
--- a/config/reliese.php
+++ b/config/reliese.php
@@ -1,10 +1,12 @@
  ['information_schema', 'performance_schema', 'mysql', 'sys']],
                     // except audit.log_.* tables
-                    ['schemas' => ['audit'], 'tables' => ['/^log_.*$/']],
+                    //['schemas' => ['audit'], 'tables' => ['/^log_.*$/']],
                     // except any table that ends in migrations or matches 'phinx' on all schemas
                     ['schemas' => [$all], 'tables' => ['/^.*migrations$/', 'phinx']],
                     // except soft delete columns on all tables for all schemas
-                    ['schemas' => [$all], 'tables' => [$all], 'columns' => ['deleted_on']]
+                    //['schemas' => [$all], 'tables' => [$all], 'columns' => ['deleted_on']]
                 ],
             ],
             /*
@@ -527,24 +529,59 @@
             ],
         ],
         // endregion Model Generator Config
+
+        // region Data Access Generator Config
+        DataAccessGeneratorConfiguration::class => [
+            'Path' => app_path().'/DataAccess/PrimaryDatabase',
+            'Namespace' => 'app\DataAccess\PrimaryDatabase',
+            //'ClassPrefix' => '',
+            'ClassSuffix' => 'DataAccess',
+            'ParentClassPrefix' => 'Abstract',
+        ],
+        // endregion Data Access Generator Config
+
+        // region Data Attribute Generator Config
+        DataAttributeGeneratorConfiguration::class => [
+            'Path' => app_path().'/DataAttribute/PrimaryDatabase',
+            'Namespace' => 'App\DataAttribute\Objects',
+            'ClassPrefix' => 'With',
+            'ClassSuffix' => 'Trait',
+            'ParentClassPrefix' => 'Abstract',
+        ],
+        // endregion Data Attribute Generator Config
+
         // region Data Transport Generator Config
-        DataTransportGeneratorConfiguration::class => [
+
+        DataTransportObjectGeneratorConfiguration::class => [
             'Path' => app_path().'/DataTransportObjects',
             'Namespace' => 'App\DataTransportObjects',
             'ClassSuffix' => 'Dto',
             'ParentClassPrefix' => 'Abstract',
+            'UseValueStateTracking' => true,
             'ObservableProperties' => [
                 'BeforeChange' => false,
                 'AfterChange' => false,
             ],
         ],
         // endregion Data Transport Generator Config
+
+        // region Data Transport Collection Generator Config
+        DataTransportCollectionGeneratorConfiguration::class => [
+            'Path' => app_path().'/DataTransport/Collections',
+            'Namespace' => 'App\DataTransport\Collections',
+            'ClassSuffix' => 'Dto',
+            'ParentClassPrefix' => 'Abstract',
+        ],
+        // endregion Data Transport Collection Generator Config
+
         // region Data Map Generator Config
         ModelDataMapGeneratorConfiguration::class => [
             'Path' => app_path().'/DataMaps/PrimaryDatabase',
             'Namespace' => 'App\DataMaps\PrimaryDatabase',
             'ClassSuffix' => 'Map',
             'ParentClassPrefix' => 'Abstract',
+            'AccessorTraitNamespace' => 'app\DataMapAccessors\PrimaryDatabase',
+            'AccessorTraitPath' => app_path().'/DataMapAccessors/PrimaryDatabase',
         ],
         // endregion Data Map Generator Config
     ]
diff --git a/src/Analyser/Doctrine/DoctrineDatabaseAnalyser.php b/src/Analyser/Doctrine/DoctrineDatabaseAnalyser.php
index 82dff7d4..2fe1ac36 100644
--- a/src/Analyser/Doctrine/DoctrineDatabaseAnalyser.php
+++ b/src/Analyser/Doctrine/DoctrineDatabaseAnalyser.php
@@ -60,7 +60,7 @@ public function analyseDatabase(DatabaseBlueprintConfiguration $databaseBlueprin
                 continue;
             }
 
-            $schemaAnalyser = $this->getSchemaAnalyser($databaseBlueprint, $schemaName);
+            $schemaAnalyser = $this->getSchemaAnalyser($databaseBlueprint, $schemaName, $databaseBlueprintConfiguration);
             $databaseBlueprint->addSchemaBlueprint(
                 // TODO: Add support for Views
                 $schemaAnalyser->analyseSchemaObjectStructures()
@@ -71,7 +71,7 @@ public function analyseDatabase(DatabaseBlueprintConfiguration $databaseBlueprin
          * Analyse foreign key constraint relationships which could potentially span schemas
          */
         foreach ($schemaNames as $schemaName) {
-            $schemaAnalyser = $this->getSchemaAnalyser($databaseBlueprint, $schemaName);
+            $schemaAnalyser = $this->getSchemaAnalyser($databaseBlueprint, $schemaName, $databaseBlueprintConfiguration);
 
             foreach ($schemaAnalyser->getTableDefinitions() as $tableName => $doctrineTableDefinition) {
 
@@ -164,7 +164,11 @@ protected function getSchemaNames(): array
      *
      * @return DoctrineSchemaAnalyser
      */
-    protected function getSchemaAnalyser(DatabaseBlueprint $databaseBlueprint, string $schemaName): DoctrineSchemaAnalyser
+    protected function getSchemaAnalyser(
+        DatabaseBlueprint $databaseBlueprint,
+        string $schemaName,
+        DatabaseBlueprintConfiguration $databaseBlueprintConfiguration
+    ): DoctrineSchemaAnalyser
     {
         if (array_key_exists($schemaName, $this->schemaAnalysers)) {
             return $this->schemaAnalysers[$schemaName];
@@ -178,7 +182,8 @@ protected function getSchemaAnalyser(DatabaseBlueprint $databaseBlueprint, strin
             $databaseBlueprint,
             $this,
             $schemaSpecificConnection,
-            $schemaSpecificDoctrineSchemaManager
+            $schemaSpecificDoctrineSchemaManager,
+            $databaseBlueprintConfiguration
         );
     }
 }
diff --git a/src/Analyser/Doctrine/DoctrineSchemaAnalyser.php b/src/Analyser/Doctrine/DoctrineSchemaAnalyser.php
index 509ccbc1..f29f96cb 100644
--- a/src/Analyser/Doctrine/DoctrineSchemaAnalyser.php
+++ b/src/Analyser/Doctrine/DoctrineSchemaAnalyser.php
@@ -16,6 +16,7 @@
 use Reliese\Blueprint\IndexBlueprint;
 use Reliese\Blueprint\SchemaBlueprint;
 use Reliese\Blueprint\TableBlueprint;
+use Reliese\Configuration\DatabaseBlueprintConfiguration;
 
 /**
  * Class DoctrineSchemaAnalyser
@@ -27,6 +28,11 @@ class DoctrineSchemaAnalyser
      */
     private DatabaseBlueprint $databaseBlueprint;
 
+    /**
+     * @var DatabaseBlueprintConfiguration
+     */
+    private DatabaseBlueprintConfiguration $databaseBlueprintConfiguration;
+
     /**
      * @var DoctrineDatabaseAnalyser
      */
@@ -66,7 +72,8 @@ public function __construct(
         DatabaseBlueprint $databaseBlueprint,
         DoctrineDatabaseAnalyser $doctrineDatabaseAnalyser,
         ConnectionInterface $connection,
-        AbstractSchemaManager $doctrineSchemaManager
+        AbstractSchemaManager $doctrineSchemaManager,
+        DatabaseBlueprintConfiguration $databaseBlueprintConfiguration
     )
     {
         $this->schemaName = $schemaName;
@@ -74,6 +81,7 @@ public function __construct(
         $this->doctrineDatabaseAnalyser = $doctrineDatabaseAnalyser;
         $this->schemaSpecificConnection = $connection;
         $this->doctrineSchemaManager = $doctrineSchemaManager;
+        $this->databaseBlueprintConfiguration = $databaseBlueprintConfiguration;
     }
 
     /**
@@ -110,6 +118,13 @@ public function analyseSchemaObjectStructures(): SchemaBlueprint
         $tableDefinitions = $this->getDoctrineSchemaManager()->listTables();
         if (!empty($tableDefinitions)) {
             foreach ($tableDefinitions as $tableDefinition) {
+                $isExcluded = $this->databaseBlueprintConfiguration
+                    ->getSchemaFilter()
+                    ->isExcludedTable($this->getSchemaName(), $tableDefinition->getName())
+                ;
+                if ($isExcluded) {
+                    continue;
+                }
                 /*
                  * Keep for future use
                  */
@@ -189,6 +204,15 @@ private function analyseTableColumns(Table $tableDefinition, TableBlueprint $tab
         }
 
         foreach ($columnDefinitions as $columnDefinition) {
+            $isExcluded = $this->databaseBlueprintConfiguration->getSchemaFilter()->isExcludedColumn(
+                $this->getSchemaName(),
+                $tableDefinition->getName(),
+                $columnDefinition->getName()
+            );
+            if ($isExcluded) {
+                continue;
+            }
+
             $columnBlueprint = $this->analyseColumn($tableBlueprint, $columnDefinition);
             $tableBlueprint->addColumnBlueprint($columnBlueprint);
         }
@@ -258,7 +282,7 @@ private function analyseIndex(TableBlueprint $tableBlueprint, Index $indexDefini
             $indexDefinition->getName(),
             $columnBlueprints,
             $indexDefinition->isPrimary(),
-            false
+            $indexDefinition->isUnique()
         );
     }
 
diff --git a/src/Blueprint/ColumnOwnerInterface.php b/src/Blueprint/ColumnOwnerInterface.php
index d896d313..c099e674 100644
--- a/src/Blueprint/ColumnOwnerInterface.php
+++ b/src/Blueprint/ColumnOwnerInterface.php
@@ -7,7 +7,7 @@
  *
  * @package Reliese\Blueprint
  */
-interface ColumnOwnerInterface
+interface ColumnOwnerInterface extends SchemaMemberInterface
 {
     /**
      * @param ColumnBlueprint $columnBlueprint
@@ -30,11 +30,4 @@ public function getColumnBlueprints(): array;
      * @return string[]
      */
     public function getColumnNames(): array;
-
-    /**
-     * returns "Schema.[TableName |View Name]"
-     *
-     * @return string
-     */
-    public function getUniqueName(): string;
 }
diff --git a/src/Blueprint/ForeignKeyBlueprint.php b/src/Blueprint/ForeignKeyBlueprint.php
index 1cc23303..be42906f 100644
--- a/src/Blueprint/ForeignKeyBlueprint.php
+++ b/src/Blueprint/ForeignKeyBlueprint.php
@@ -101,4 +101,28 @@ public function getReferencedTableBlueprint() : ColumnOwnerInterface
     {
         return $this->referencedTable;
     }
+
+    /**
+     * @return ColumnBlueprint[]
+     */
+    public function getReferencedColumns() : array
+    {
+        return $this->referencedColumns;
+    }
+
+    public function getFkColumnPairs():array
+    {
+        $columns = [];
+        $i = -1;
+        foreach ($this->referencingColumns as $referencingColumn) {
+            $i++;
+            $columns[$i][0] = $referencingColumn;
+        }
+        $i = -1;
+        foreach ($this->referencedColumns as $referencedColumn) {
+            $i++;
+            $columns[$i][1] = $referencedColumn;
+        }
+        return $columns;
+    }
 }
diff --git a/src/Blueprint/IndexBlueprint.php b/src/Blueprint/IndexBlueprint.php
index 20312ef9..29ffd83e 100644
--- a/src/Blueprint/IndexBlueprint.php
+++ b/src/Blueprint/IndexBlueprint.php
@@ -8,6 +8,7 @@
 class IndexBlueprint implements ColumnOwnerInterface
 {
     use ColumnOwnerTrait;
+    use SchemaMemberTrait;
 
     /**
      * @var string
@@ -79,4 +80,14 @@ public function isUnique(): bool
     {
         return $this->isUnique || $this->isPrimaryKey();
     }
+
+    public function getSchemaMemberType(): SchemaMemberType
+    {
+        return SchemaMemberType::Index();
+    }
+
+    public function getName(): string
+    {
+        return $this->indexName;
+    }
 }
diff --git a/src/Blueprint/SchemaMemberInterface.php b/src/Blueprint/SchemaMemberInterface.php
index 7bdde788..bc2a6c26 100644
--- a/src/Blueprint/SchemaMemberInterface.php
+++ b/src/Blueprint/SchemaMemberInterface.php
@@ -18,6 +18,11 @@ interface SchemaMemberInterface
      */
     public function getName(): string;
 
+    /**
+     * @return string
+     */
+    public function getUniqueName(): string;
+
     /**
      * Returns an instance of SchemaMemberType
      *
diff --git a/src/Blueprint/SchemaMemberTrait.php b/src/Blueprint/SchemaMemberTrait.php
index 85af65b4..7b149a4b 100644
--- a/src/Blueprint/SchemaMemberTrait.php
+++ b/src/Blueprint/SchemaMemberTrait.php
@@ -54,4 +54,18 @@ public function setName(string $name)
     {
         $this->name = $name;
     }
+
+    /**
+     * @return string
+     */
+    public function getUniqueName(): string
+    {
+        if (empty($this->getSchemaBlueprint()->getSchemaName())) {
+            return $this->getName();
+        }
+
+        return sprintf('%s.%s',
+            $this->getSchemaBlueprint()->getSchemaName(),
+            $this->getName());
+    }
 }
\ No newline at end of file
diff --git a/src/Blueprint/TableBlueprint.php b/src/Blueprint/TableBlueprint.php
index 92a08353..5e7ee474 100644
--- a/src/Blueprint/TableBlueprint.php
+++ b/src/Blueprint/TableBlueprint.php
@@ -97,16 +97,6 @@ public function getSchemaMemberType(): SchemaMemberType
         return SchemaMemberType::Table();
     }
 
-    /**
-     * @return string
-     */
-    public function getUniqueName(): string
-    {
-        return sprintf('%s.%s',
-            $this->getSchemaBlueprint()->getSchemaName(),
-            $this->getName());
-    }
-
     /**
      * @return ForeignKeyBlueprint[]
      */
@@ -126,4 +116,54 @@ public function getForeignKeyBlueprintsGroupedByReferencedTable() : array
         }
         return $results;
     }
+
+    /**
+     * @param bool $includePrimaryKey
+     *
+     * @return array[]
+     */
+    public function getUniqueColumnGroups(bool $includePrimaryKey = true): array
+    {
+        $uniqueColumnGroups = [];
+
+        foreach ($this->indexBlueprints as $indexBlueprint) {
+            if ($indexBlueprint->isPrimaryKey() && !$includePrimaryKey) {
+                continue;
+            }
+            if (!$indexBlueprint->isUnique()) {
+                continue;
+            }
+
+            $uniqueColumnGroups[] = $indexBlueprint->getColumnBlueprints();
+        }
+
+        return $uniqueColumnGroups;
+    }
+
+    /**
+     * @param bool $includePrimaryKey
+     *
+     * @return IndexBlueprint[]
+     */
+    public function getUniqueIndexes(bool $includePrimaryKey = true): array
+    {
+        $uniqueIndexes = [];
+
+        foreach ($this->indexBlueprints as $indexBlueprint) {
+            if ($indexBlueprint->isPrimaryKey() && !$includePrimaryKey) {
+                continue;
+            }
+            if (!$indexBlueprint->isUnique()) {
+                continue;
+            }
+
+            $uniqueIndexes[] = $indexBlueprint;
+        }
+
+        usort($uniqueIndexes, function (IndexBlueprint $a, IndexBlueprint $b) {
+            return strncasecmp($a->getName(),
+            $b->getName(), mb_strlen($a->getName()));});
+
+        return $uniqueIndexes;
+    }
 }
diff --git a/src/Command/DataAccess/DataAccessGenerateCommand.php b/src/Command/DataAccess/DataAccessGenerateCommand.php
new file mode 100644
index 00000000..82658701
--- /dev/null
+++ b/src/Command/DataAccess/DataAccessGenerateCommand.php
@@ -0,0 +1,140 @@
+signature .= self::$configurationProfileOptionDescription;
+        parent::__construct();
+
+        $this->models = $models;
+        $this->config = $config;
+    }
+
+    /**
+     * Execute the console command.
+     *
+     * @param AnalyserFactory $analyserFactory
+     * @param RelieseConfigurationFactory $relieseConfigurationFactory
+     */
+    public function handle(
+        AnalyserFactory $analyserFactory,
+        RelieseConfigurationFactory $relieseConfigurationFactory,
+    ) {
+        $relieseConfiguration = $relieseConfigurationFactory->getRelieseConfiguration($this->getConfigurationProfileName());
+        $connection = $this->getConnection();
+        $schema = $this->getSchema($connection);
+        $table = $this->getTable();
+
+        /*
+         * TODO: allow command line options to modify state of the $relieseConfiguration graph
+         */
+
+        /*
+         * Create the correct analyser for the configuration profile
+         */
+        $databaseAnalyser =  $analyserFactory->databaseAnalyser($relieseConfiguration);
+
+        /*
+         * Allow the $databaseAnalyser to create the Database Blueprint
+         */
+        $databaseBlueprint = $databaseAnalyser->analyseDatabase($relieseConfiguration->getDatabaseBlueprintConfiguration());
+
+        /*
+         * Generate class files
+         */
+        $dataAccessGenerator = new DataAccessGenerator($relieseConfiguration);
+
+        $schemaBlueprint = $databaseBlueprint->getSchemaBlueprint($schema);
+
+        if (!empty($table)) {
+            // Generate only for the specified table
+            $tableBlueprint = $schemaBlueprint->getTableBlueprint($table);
+            $dataAccessGenerator->fromTableBlueprint($tableBlueprint);
+            return;
+        }
+
+        /*
+         * Display the data that would be used to perform code generation
+         */
+        foreach ($schemaBlueprint->getTableBlueprints() as $tableBlueprint) {
+            $dataAccessGenerator->fromTableBlueprint($tableBlueprint);
+        }
+    }
+
+    /**
+     * @return string
+     */
+    protected function getConnection()
+    {
+        return $this->option('connection') ?: $this->config->get('database.default');
+    }
+
+    /**
+     * @param $connection
+     *
+     * @return string
+     */
+    protected function getSchema($connection)
+    {
+        return $this->option('schema') ?: $this->config->get("database.connections.$connection.database");
+    }
+
+    /**
+     * @return string
+     */
+    protected function getTable()
+    {
+        return $this->option('table');
+    }
+}
diff --git a/src/Command/DataAttribute/DataAttributeGenerateCommand.php b/src/Command/DataAttribute/DataAttributeGenerateCommand.php
new file mode 100644
index 00000000..9478957e
--- /dev/null
+++ b/src/Command/DataAttribute/DataAttributeGenerateCommand.php
@@ -0,0 +1,131 @@
+signature .= self::$configurationProfileOptionDescription;
+        parent::__construct();
+
+        $this->models = $models;
+        $this->config = $config;
+    }
+
+    /**
+     * Execute the console command.
+     *
+     * @param AnalyserFactory $analyserFactory
+     * @param RelieseConfigurationFactory $relieseConfigurationFactory
+     */
+    public function handle(
+        AnalyserFactory $analyserFactory,
+        RelieseConfigurationFactory $relieseConfigurationFactory,
+    ) {
+        $relieseConfiguration = $relieseConfigurationFactory->getRelieseConfiguration($this->getConfigurationProfileName());
+        $connection = $this->getConnection();
+        $schema = $this->getSchema($connection);
+        $table = $this->getTable();
+
+        /*
+         * TODO: allow command line options to modify state of the $relieseConfiguration graph
+         */
+
+        /*
+         * Create the correct analyser for the configuration profile
+         */
+        $databaseAnalyser =  $analyserFactory->databaseAnalyser($relieseConfiguration);
+
+        /*
+         * Allow the $databaseAnalyser to create the Database Blueprint
+         */
+        $databaseBlueprint = $databaseAnalyser->analyseDatabase($relieseConfiguration->getDatabaseBlueprintConfiguration());
+
+        /*
+         * Generate class files
+         */
+        $dataAttributeGenerator = new DataAttributeGenerator($relieseConfiguration);
+
+        $schemaBlueprint = $databaseBlueprint->getSchemaBlueprint($schema);
+
+        /*
+         * Display the data that would be used to perform code generation
+         */
+        foreach ($schemaBlueprint->getTableBlueprints() as $tableBlueprint) {
+            $dataAttributeGenerator->fromColumnBlueprint($tableBlueprint);
+        }
+    }
+
+    /**
+     * @return string
+     */
+    protected function getConnection()
+    {
+        return $this->option('connection') ?: $this->config->get('database.default');
+    }
+
+    /**
+     * @param $connection
+     *
+     * @return string
+     */
+    protected function getSchema($connection)
+    {
+        return $this->option('schema') ?: $this->config->get("database.connections.$connection.database");
+    }
+
+    /**
+     * @return string
+     */
+    protected function getTable()
+    {
+        return $this->option('table');
+    }
+}
diff --git a/src/Command/DataMap/ModelDataMapGenerateCommand.php b/src/Command/DataMap/ModelDataMapGenerateCommand.php
index b7808cba..b1a4b21e 100644
--- a/src/Command/DataMap/ModelDataMapGenerateCommand.php
+++ b/src/Command/DataMap/ModelDataMapGenerateCommand.php
@@ -9,8 +9,9 @@
 use Reliese\Coders\Model\Factory;
 use Reliese\Command\ConfigurationProfileOptionTrait;
 use Reliese\Configuration\RelieseConfigurationFactory;
+use Reliese\Generator\DataAttribute\DataAttributeGenerator;
 use Reliese\Generator\DataMap\ModelDataMapGenerator;
-use Reliese\Generator\DataTransport\DataTransportGenerator;
+use Reliese\Generator\DataTransport\DataTransportObjectGenerator;
 use Reliese\Generator\Model\ModelGenerator;
 
 /**
@@ -86,9 +87,7 @@ public function handle(
         /*
          * Create the correct analyser for the configuration profile
          */
-        $databaseAnalyser =  $analyserFactory->databaseAnalyser(
-            $relieseConfiguration
-        );
+        $databaseAnalyser =  $analyserFactory->databaseAnalyser($relieseConfiguration);
 
         /*
          * Allow the $databaseAnalyser to create the Database Blueprint
@@ -102,11 +101,7 @@ public function handle(
         /*
          * Create a ModelDataMapGenerator
          */
-        $modelDataMapGenerator = new ModelDataMapGenerator(
-            $relieseConfiguration->getModelDataMapGeneratorConfiguration(),
-            new ModelGenerator($relieseConfiguration->getModelGeneratorConfiguration()),
-            new DataTransportGenerator($relieseConfiguration->getDataTransportGeneratorConfiguration()),
-        );
+        $modelDataMapGenerator = new ModelDataMapGenerator($relieseConfiguration);
 
         if (!empty($table)) {
             // Generate only for the specified table
diff --git a/src/Command/DataTransport/DataTransportGenerateCommand.php b/src/Command/DataTransport/DataTransportGenerateCommand.php
index e5e8a9e9..42e2c77a 100644
--- a/src/Command/DataTransport/DataTransportGenerateCommand.php
+++ b/src/Command/DataTransport/DataTransportGenerateCommand.php
@@ -9,7 +9,8 @@
 use Reliese\Coders\Model\Factory;
 use Reliese\Command\ConfigurationProfileOptionTrait;
 use Reliese\Configuration\RelieseConfigurationFactory;
-use Reliese\Generator\DataTransport\DataTransportGenerator;
+use Reliese\Generator\DataAttribute\DataAttributeGenerator;
+use Reliese\Generator\DataTransport\DataTransportObjectGenerator;
 
 /**
  * Class DataTransportGenerateCommand
@@ -94,9 +95,7 @@ public function handle(
         /*
          * Generate class files
          */
-        $dataTransportGenerator = new DataTransportGenerator(
-            $relieseConfiguration->getDataTransportGeneratorConfiguration()
-        );
+        $dataTransportGenerator = new DataTransportObjectGenerator($relieseConfiguration);
 
         $schemaBlueprint = $databaseBlueprint->getSchemaBlueprint($schema);
 
diff --git a/src/Command/Model/NewModelGenerateCommand.php b/src/Command/Model/NewModelGenerateCommand.php
index 43beba85..00312c5a 100644
--- a/src/Command/Model/NewModelGenerateCommand.php
+++ b/src/Command/Model/NewModelGenerateCommand.php
@@ -100,9 +100,7 @@ public function handle(
         /*
          * Create the correct analyser for the configuration profile
          */
-        $databaseAnalyser =  $analyserFactory->databaseAnalyser(
-            $relieseConfiguration
-        );
+        $databaseAnalyser =  $analyserFactory->databaseAnalyser($relieseConfiguration);
 
         /*
          * Allow the $databaseAnalyser to create the Database Blueprint
@@ -110,7 +108,7 @@ public function handle(
         $databaseBlueprint = $databaseAnalyser->analyseDatabase($relieseConfiguration->getDatabaseBlueprintConfiguration());
 
         // TODO: Apply Command Line options that override the configuration values
-        $modelGenerator = new ModelGenerator($relieseConfiguration->getModelGeneratorConfiguration());
+        $modelGenerator = new ModelGenerator($relieseConfiguration);
 
         $schemaBlueprint = $databaseBlueprint->getSchemaBlueprint($schema);
 
diff --git a/src/Configuration/DataAccessGeneratorConfiguration.php b/src/Configuration/DataAccessGeneratorConfiguration.php
new file mode 100644
index 00000000..b511ba20
--- /dev/null
+++ b/src/Configuration/DataAccessGeneratorConfiguration.php
@@ -0,0 +1,88 @@
+path = $configuration['Path'];
+        $this->namespace = $configuration['Namespace'];
+        $this->classPrefix = $configuration['ClassPrefix'] ?? '';
+        $this->classSuffix = $configuration['ClassSuffix'] ?? '';
+        $this->parentClassPrefix = $configuration['ParentClassPrefix'] ?? '';
+    }
+
+    /**
+     * @return mixed|string
+     */
+    public function getClassPrefix(): mixed
+    {
+        return $this->classPrefix;
+    }
+
+    /**
+     * @return mixed
+     */
+    public function getClassSuffix(): mixed
+    {
+        return $this->classSuffix;
+    }
+
+    /**
+     * @return string
+     */
+    public function getNamespace(): string
+    {
+        return $this->namespace;
+    }
+
+    /**
+     * @return mixed
+     */
+    public function getParentClassPrefix(): mixed
+    {
+        return $this->parentClassPrefix;
+    }
+
+    /**
+     * @return string
+     */
+    public function getPath(): string
+    {
+        return $this->path;
+    }
+}
diff --git a/src/Configuration/DataAttributeGeneratorConfiguration.php b/src/Configuration/DataAttributeGeneratorConfiguration.php
new file mode 100644
index 00000000..7c16c8f1
--- /dev/null
+++ b/src/Configuration/DataAttributeGeneratorConfiguration.php
@@ -0,0 +1,74 @@
+path = $configuration['Path'];
+        $this->namespace = $configuration['Namespace'];
+        $this->traitPrefix = $configuration['TraitPrefix'] ?? "";
+        $this->traitSuffix = $configuration['TraitSuffix'] ?? "";
+    }
+
+    /**
+     * @return mixed
+     */
+    public function getTraitPrefix(): mixed
+    {
+        return $this->traitPrefix;
+    }
+
+    /**
+     * @return mixed
+     */
+    public function getTraitSuffix(): mixed
+    {
+        return $this->traitSuffix;
+    }
+
+    /**
+     * @return string
+     */
+    public function getNamespace(): string
+    {
+        return $this->namespace;
+    }
+
+    /**
+     * @return string
+     */
+    public function getPath(): string
+    {
+        return $this->path;
+    }
+}
diff --git a/src/Configuration/DataTransportCollectionGeneratorConfiguration.php b/src/Configuration/DataTransportCollectionGeneratorConfiguration.php
new file mode 100644
index 00000000..663212e7
--- /dev/null
+++ b/src/Configuration/DataTransportCollectionGeneratorConfiguration.php
@@ -0,0 +1,74 @@
+path = $configuration['Path'];
+        $this->namespace = $configuration['Namespace'];
+        $this->classSuffix = $configuration['ClassSuffix'] ?? '';
+        $this->parentClassPrefix = $configuration['ParentClassPrefix'] ?? '';
+    }
+
+    /**
+     * @return mixed
+     */
+    public function getClassSuffix(): mixed
+    {
+        return $this->classSuffix;
+    }
+
+    /**
+     * @return string
+     */
+    public function getNamespace(): string
+    {
+        return $this->namespace;
+    }
+
+    /**
+     * @return mixed
+     */
+    public function getParentClassPrefix(): mixed
+    {
+        return $this->parentClassPrefix;
+    }
+
+    /**
+     * @return string
+     */
+    public function getPath(): string
+    {
+        return $this->path;
+    }
+}
diff --git a/src/Configuration/DataTransportGeneratorConfiguration.php b/src/Configuration/DataTransportGeneratorConfiguration.php
index 007b99b3..34aaac17 100644
--- a/src/Configuration/DataTransportGeneratorConfiguration.php
+++ b/src/Configuration/DataTransportGeneratorConfiguration.php
@@ -3,109 +3,10 @@
 namespace Reliese\Configuration;
 
 /**
- * Class DataTransportGeneratorConfiguration
+ * Class DataTransportObjectGeneratorConfiguration
  *
  * @deprecated Please use DataTransportObjectGeneratorConfiguration
  */
-class DataTransportGeneratorConfiguration
+class DataTransportGeneratorConfiguration extends DataTransportObjectGeneratorConfiguration
 {
-    /**
-     * @var string
-     */
-    private string $classSuffix;
-
-    /**
-     * @var string
-     */
-    private string $namespace;
-
-    /**
-     * @var string
-     */
-    private string $parentClassPrefix;
-
-    /**
-     * @var string
-     */
-    private string $path;
-
-    /**
-     * @var bool
-     */
-    private bool $useBeforeChangeObservableProperties = false;
-
-    /**
-     * @var bool
-     */
-    private bool $useAfterChangeObservableProperties = false;
-
-    /**
-     * DataTransportGeneratorConfiguration constructor.
-     *
-     * @param array $configuration
-     */
-    public function __construct(array $configuration)
-    {
-        $this->path = $configuration['Path'];
-        $this->namespace = $configuration['Namespace'];
-        $this->classSuffix = $configuration['ClassSuffix'];
-        $this->parentClassPrefix = $configuration['ParentClassPrefix'];
-        if (\array_key_exists('ObservableProperties', $configuration)) {
-            $observableConfig = $configuration['ObservableProperties'];
-            if (\array_key_exists('BeforeChange', $observableConfig)) {
-                $this->useBeforeChangeObservableProperties = $observableConfig['BeforeChange'];
-            }
-            if (\array_key_exists('AfterChange', $observableConfig)) {
-                $this->useBeforeChangeObservableProperties = $observableConfig['AfterChange'];
-            }
-        }
-    }
-
-    /**
-     * @return mixed
-     */
-    public function getClassSuffix(): mixed
-    {
-        return $this->classSuffix;
-    }
-
-    /**
-     * @return string
-     */
-    public function getNamespace(): string
-    {
-        return $this->namespace;
-    }
-
-    /**
-     * @return mixed
-     */
-    public function getParentClassPrefix(): mixed
-    {
-        return $this->parentClassPrefix;
-    }
-
-    /**
-     * @return string
-     */
-    public function getPath(): string
-    {
-        return $this->path;
-    }
-
-    /**
-     * @return bool
-     */
-    public function useAfterChangeObservableProperties(): bool
-    {
-        return $this->useAfterChangeObservableProperties;
-    }
-
-    /**
-     * @return bool
-     */
-    public function useBeforeChangeObservableProperties(): bool
-    {
-        return $this->useBeforeChangeObservableProperties;
-    }
-}
+}
\ No newline at end of file
diff --git a/src/Configuration/DataTransportObjectGeneratorConfiguration.php b/src/Configuration/DataTransportObjectGeneratorConfiguration.php
index 27f39613..f8c70841 100644
--- a/src/Configuration/DataTransportObjectGeneratorConfiguration.php
+++ b/src/Configuration/DataTransportObjectGeneratorConfiguration.php
@@ -3,10 +3,118 @@
 namespace Reliese\Configuration;
 
 /**
- * Class DataTransportObjectGeneratorConfiguration
- * This class was created to facilitate renaming of DataTransportGeneratorConfiguration to
- * DataTransportObjectGeneratorConfiguration
+ * Class DataTransportGeneratorConfiguration
  */
-class DataTransportObjectGeneratorConfiguration extends DataTransportGeneratorConfiguration
+class DataTransportObjectGeneratorConfiguration
 {
-}
\ No newline at end of file
+    /**
+     * @var string
+     */
+    private string $classSuffix;
+
+    /**
+     * @var string
+     */
+    private string $namespace;
+
+    /**
+     * @var string
+     */
+    private string $parentClassPrefix;
+
+    /**
+     * @var string
+     */
+    private string $path;
+
+    /**
+     * DataTransportObjectGeneratorConfiguration constructor.
+     * @var bool
+     */
+    private bool $useBeforeChangeObservableProperties = false;
+
+    /**
+     * @var bool
+     */
+    private bool $useAfterChangeObservableProperties = false;
+
+    private $useValueStateTracking;
+
+    /**
+     * DataTransportGeneratorConfiguration constructor.
+     *
+     * @param array $configuration
+     */
+    public function __construct(array $configuration)
+    {
+        $this->path = $configuration['Path'];
+        $this->namespace = $configuration['Namespace'];
+        $this->classSuffix = $configuration['ClassSuffix'] ?? '';
+        $this->parentClassPrefix = $configuration['ParentClassPrefix'] ?? '';
+        $this->classSuffix = $configuration['ClassSuffix'];
+        $this->parentClassPrefix = $configuration['ParentClassPrefix'];
+        $this->useValueStateTracking = $configuration['UseValueStateTracking'] ?? false;
+        if (\array_key_exists('ObservableProperties', $configuration)) {
+            $observableConfig = $configuration['ObservableProperties'];
+            if (\array_key_exists('BeforeChange', $observableConfig)) {
+                $this->useBeforeChangeObservableProperties = true === $observableConfig['BeforeChange'];
+            }
+            if (\array_key_exists('AfterChange', $observableConfig)) {
+                $this->useAfterChangeObservableProperties = true === $observableConfig['AfterChange'];
+            }
+        }
+    }
+
+    /**
+     * @return mixed
+     */
+    public function getClassSuffix(): mixed
+    {
+        return $this->classSuffix;
+    }
+
+    /**
+     * @return string
+     */
+    public function getNamespace(): string
+    {
+        return $this->namespace;
+    }
+
+    /**
+     * @return mixed
+     */
+    public function getParentClassPrefix(): mixed
+    {
+        return $this->parentClassPrefix;
+    }
+
+    /**
+     * @return string
+     */
+    public function getPath(): string
+    {
+        return $this->path;
+    }
+
+    /**
+     * @return bool
+     */
+    public function getUseAfterChangeObservableProperties(): bool
+    {
+        return $this->useAfterChangeObservableProperties;
+    }
+
+    /**
+     * @return bool
+     */
+    public function getUseBeforeChangeObservableProperties(): bool
+    {
+        return $this->useBeforeChangeObservableProperties;
+    }
+
+    public function getUseValueStateTracking(): bool
+    {
+        return $this->useValueStateTracking;
+    }
+}
diff --git a/src/Configuration/ModelDataMapGeneratorConfiguration.php b/src/Configuration/ModelDataMapGeneratorConfiguration.php
index fa425366..c56b6f61 100644
--- a/src/Configuration/ModelDataMapGeneratorConfiguration.php
+++ b/src/Configuration/ModelDataMapGeneratorConfiguration.php
@@ -7,6 +7,16 @@
  */
 class ModelDataMapGeneratorConfiguration
 {
+    /**
+     * @var mixed|string
+     */
+    private $accessorTraitNamespace;
+
+    /**
+     * @var mixed|string
+     */
+    private $accessorTraitPath;
+
     /**
      * @var mixed
      */
@@ -28,17 +38,18 @@ class ModelDataMapGeneratorConfiguration
     private string $path;
 
     /**
-     * DataTransportGeneratorConfiguration constructor.
+     * DataTransportObjectGeneratorConfiguration constructor.
      *
      * @param array $configuration
      */
     public function __construct(array $configuration)
     {
-
         $this->path = $configuration['Path'];
         $this->namespace = $configuration['Namespace'];
-        $this->classSuffix = $configuration['ClassSuffix'];
-        $this->parentClassPrefix = $configuration['ParentClassPrefix'];
+        $this->classSuffix = $configuration['ClassSuffix'] ?? '';
+        $this->parentClassPrefix = $configuration['ParentClassPrefix'] ?? '';
+        $this->accessorTraitNamespace = $configuration['AccessorTraitNamespace'] ?? '';
+        $this->accessorTraitPath = $configuration['AccessorTraitPath'] ?? '';
     }
 
     /**
@@ -72,4 +83,20 @@ public function getPath(): string
     {
         return $this->path;
     }
+
+    /**
+     * @return ?string
+     */
+    public function getAccessorTraitNamespace(): ?string
+    {
+        return $this->accessorTraitNamespace;
+    }
+
+    /**
+     * @return mixed|string
+     */
+    public function getAccessorTraitPath(): mixed
+    {
+        return $this->accessorTraitPath;
+    }
 }
diff --git a/src/Configuration/RelieseConfiguration.php b/src/Configuration/RelieseConfiguration.php
index 669af714..e673f465 100644
--- a/src/Configuration/RelieseConfiguration.php
+++ b/src/Configuration/RelieseConfiguration.php
@@ -13,9 +13,9 @@ class RelieseConfiguration
     protected ModelDataMapGeneratorConfiguration $modelDataMapGeneratorConfiguration;
 
     /**
-     * @var DataTransportGeneratorConfiguration
+     * @var DataTransportObjectGeneratorConfiguration
      */
-    protected DataTransportGeneratorConfiguration $dataTransportGeneratorConfiguration;
+    protected DataTransportObjectGeneratorConfiguration $dataTransportGeneratorConfiguration;
 
     /**
      * @var DatabaseAnalyserConfiguration
@@ -37,29 +37,45 @@ class RelieseConfiguration
      */
     private string $configurationProfileName;
 
+    /**
+     * @var DataAccessGeneratorConfiguration
+     */
+    private DataAccessGeneratorConfiguration $dataAccessGeneratorConfiguration;
+
+    /**
+     * @var DataAttributeGeneratorConfiguration
+     */
+    private DataAttributeGeneratorConfiguration $dataAttributeGeneratorConfiguration;
+
     /**
      * RelieseConfiguration constructor.
      *
      * @param string $configurationProfileName
      * @param ModelDataMapGeneratorConfiguration $modelDataMapGeneratorConfiguration
-     * @param DataTransportGeneratorConfiguration $dataTransportGeneratorConfiguration
+     * @param DataAccessGeneratorConfiguration $dataAccessGeneratorConfiguration
+     * @param DataTransportObjectGeneratorConfiguration $dataTransportGeneratorConfiguration
      * @param DatabaseAnalyserConfiguration $databaseAnalyserConfiguration
+     * @param DataAttributeGeneratorConfiguration $dataAttributeGeneratorConfiguration
      * @param DatabaseBlueprintConfiguration $databaseBlueprintConfiguration
      * @param ModelGeneratorConfiguration $modelGeneratorConfiguration
      */
     public function __construct(string $configurationProfileName,
         ModelDataMapGeneratorConfiguration $modelDataMapGeneratorConfiguration,
-        DataTransportGeneratorConfiguration $dataTransportGeneratorConfiguration,
+        DataAccessGeneratorConfiguration $dataAccessGeneratorConfiguration,
+        DataTransportObjectGeneratorConfiguration $dataTransportGeneratorConfiguration,
         DatabaseAnalyserConfiguration $databaseAnalyserConfiguration,
+        DataAttributeGeneratorConfiguration $dataAttributeGeneratorConfiguration,
         DatabaseBlueprintConfiguration $databaseBlueprintConfiguration,
         ModelGeneratorConfiguration $modelGeneratorConfiguration,)
     {
         $this->configurationProfileName = $configurationProfileName;
         $this->modelDataMapGeneratorConfiguration = $modelDataMapGeneratorConfiguration;
+        $this->dataAccessGeneratorConfiguration = $dataAccessGeneratorConfiguration;
         $this->dataTransportGeneratorConfiguration = $dataTransportGeneratorConfiguration;
         $this->databaseAnalyserConfiguration = $databaseAnalyserConfiguration;
         $this->databaseBlueprintConfiguration = $databaseBlueprintConfiguration;
         $this->modelGeneratorConfiguration = $modelGeneratorConfiguration;
+        $this->dataAttributeGeneratorConfiguration = $dataAttributeGeneratorConfiguration;
     }
 
     /**
@@ -70,6 +86,19 @@ public function getConfigurationProfileName(): string
         return $this->configurationProfileName;
     }
 
+    public function getDataAccessGeneratorConfiguration()
+    {
+        return $this->dataAccessGeneratorConfiguration;
+    }
+
+    /**
+     * @return DataAttributeGeneratorConfiguration
+     */
+    public function getDataAttributeGeneratorConfiguration(): DataAttributeGeneratorConfiguration
+    {
+        return $this->dataAttributeGeneratorConfiguration;
+    }
+
     /**
      * @return ModelDataMapGeneratorConfiguration
      */
@@ -79,9 +108,9 @@ public function getModelDataMapGeneratorConfiguration(): ModelDataMapGeneratorCo
     }
 
     /**
-     * @return DataTransportGeneratorConfiguration
+     * @return DataTransportObjectGeneratorConfiguration
      */
-    public function getDataTransportGeneratorConfiguration(): DataTransportGeneratorConfiguration
+    public function getDataTransportGeneratorConfiguration(): DataTransportObjectGeneratorConfiguration
     {
         return $this->dataTransportGeneratorConfiguration;
     }
diff --git a/src/Configuration/RelieseConfigurationFactory.php b/src/Configuration/RelieseConfigurationFactory.php
index b007d531..3323d040 100644
--- a/src/Configuration/RelieseConfigurationFactory.php
+++ b/src/Configuration/RelieseConfigurationFactory.php
@@ -2,7 +2,6 @@
 
 namespace Reliese\Configuration;
 
-use Illuminate\Support\Facades\Log;
 use InvalidArgumentException;
 use Reliese\PackagePaths;
 use function array_key_exists;
@@ -54,6 +53,11 @@ public function __construct(
         $this->relieseConfigurationProfiles = $relieseConfigurationProfiles ?? include(PackagePaths::getExampleConfigFilePath());
     }
 
+    /**
+     * @param string $configurationProfileName
+     *
+     * @return RelieseConfiguration
+     */
     public function getRelieseConfiguration(string $configurationProfileName): RelieseConfiguration
     {
 // TODO: figure out how to make logging work w/ tests as well
@@ -67,36 +71,54 @@ public function getRelieseConfiguration(string $configurationProfileName): Relie
 
         return new RelieseConfiguration(
             $configurationProfileName,
-            $this->getDataMapGeneratorConfiguration($configurationProfile),
-            $this->getDataTransportGeneratorConfiguration($configurationProfile),
+            $this->getModelDataMapGeneratorConfiguration($configurationProfile),
+            $this->getDataAccessGeneratorConfiguration($configurationProfile),
+            $this->getDataTransportObjectGeneratorConfiguration($configurationProfile),
             $this->getDatabaseAnalyserConfiguration($configurationProfile),
+            $this->getDataAttributeGeneratorConfiguration($configurationProfile),
             $this->getDatabaseBlueprintConfiguration($configurationProfile),
             $this->getModelGeneratorConfiguration($configurationProfile)
         );
     }
 
-    protected function getDataMapGeneratorConfiguration(array $configurationProfile): ModelDataMapGeneratorConfiguration
+    /**
+     * @param array $configurationProfile
+     *
+     * @return DataAttributeGeneratorConfiguration
+     */
+    protected function getDataAttributeGeneratorConfiguration(array $configurationProfile): DataAttributeGeneratorConfiguration
     {
-        $key = ModelDataMapGeneratorConfiguration::class;
+        $key = DataAttributeGeneratorConfiguration::class;
 
         if (!array_key_exists($key, $configurationProfile)) {
             throw new InvalidArgumentException("Unable to locate configuration block for \"{$key}\"");
         }
 
-        return new ModelDataMapGeneratorConfiguration($configurationProfile[$key]);
+        return new DataAttributeGeneratorConfiguration($configurationProfile[$key]);
     }
 
-    protected function getDataTransportGeneratorConfiguration(array $configurationProfile): DataTransportGeneratorConfiguration
+    /**
+     * @param array $configurationProfile
+     *
+     * @return DataTransportObjectGeneratorConfiguration
+     */
+    protected function getDataTransportObjectGeneratorConfiguration(array $configurationProfile): DataTransportObjectGeneratorConfiguration
     {
-        $key = DataTransportGeneratorConfiguration::class;
+
+        $key = DataTransportObjectGeneratorConfiguration::class;
 
         if (!array_key_exists($key, $configurationProfile)) {
             throw new InvalidArgumentException("Unable to locate configuration block for \"{$key}\"");
         }
 
-        return new DataTransportGeneratorConfiguration($configurationProfile[$key]);
+        return new DataTransportObjectGeneratorConfiguration($configurationProfile[$key]);
     }
 
+    /**
+     * @param array $configurationProfile
+     *
+     * @return DatabaseAnalyserConfiguration
+     */
     protected function getDatabaseAnalyserConfiguration(array $configurationProfile): DatabaseAnalyserConfiguration
     {
         $key = DatabaseAnalyserConfiguration::class;
@@ -108,6 +130,11 @@ protected function getDatabaseAnalyserConfiguration(array $configurationProfile)
         return new DatabaseAnalyserConfiguration($configurationProfile[$key]);
     }
 
+    /**
+     * @param array $configurationProfile
+     *
+     * @return DatabaseBlueprintConfiguration
+     */
     protected function getDatabaseBlueprintConfiguration(array $configurationProfile): DatabaseBlueprintConfiguration
     {
         $key = DatabaseBlueprintConfiguration::class;
@@ -119,6 +146,26 @@ protected function getDatabaseBlueprintConfiguration(array $configurationProfile
         return new DatabaseBlueprintConfiguration($configurationProfile[$key]);
     }
 
+    /**
+     * @param array $configurationProfile
+     *
+     * @return ModelDataMapGeneratorConfiguration
+     */
+    protected function getModelDataMapGeneratorConfiguration(array $configurationProfile): ModelDataMapGeneratorConfiguration
+    {
+        $key = ModelDataMapGeneratorConfiguration::class;
+        if (!array_key_exists($key, $configurationProfile)) {
+            throw new InvalidArgumentException("Unable to locate configuration block for \"$key\"");
+        }
+
+        return new ModelDataMapGeneratorConfiguration($configurationProfile[$key]);
+    }
+
+    /**
+     * @param array $configurationProfile
+     *
+     * @return ModelGeneratorConfiguration
+     */
     protected function getModelGeneratorConfiguration(array $configurationProfile): ModelGeneratorConfiguration
     {
         $key = ModelGeneratorConfiguration::class;
@@ -129,4 +176,19 @@ protected function getModelGeneratorConfiguration(array $configurationProfile):
 
         return new ModelGeneratorConfiguration($configurationProfile[$key]);
     }
+
+    /**
+     * @param mixed $configurationProfile
+     *
+     * @return DataAccessGeneratorConfiguration
+     */
+    private function getDataAccessGeneratorConfiguration(mixed $configurationProfile): DataAccessGeneratorConfiguration
+    {
+        $key = DataAccessGeneratorConfiguration::class;
+        if (!array_key_exists($key, $configurationProfile)) {
+            throw new InvalidArgumentException("Unable to locate configuration block for \"$key\"");
+        }
+
+        return new DataAccessGeneratorConfiguration($configurationProfile[$key]);
+    }
 }
diff --git a/src/Generator/DataAccess/DataAccessGenerator.php b/src/Generator/DataAccess/DataAccessGenerator.php
new file mode 100644
index 00000000..19cf2d90
--- /dev/null
+++ b/src/Generator/DataAccess/DataAccessGenerator.php
@@ -0,0 +1,783 @@
+dataAccessGeneratorConfiguration = $relieseConfiguration->getDataAccessGeneratorConfiguration();
+        /*
+         * TODO: inject a MySql / Postgress or other DataType mapping as needed
+         */
+        $this->dataTypeMap = new MySqlDataTypeMap();
+        $this->modelGenerator = new ModelGenerator($relieseConfiguration);
+        $this->dataTransportObjectGenerator = new DataTransportObjectGenerator($relieseConfiguration);
+        $this->modelDataMapGenerator = new ModelDataMapGenerator($relieseConfiguration);
+    }
+
+    /**
+     * @param TableBlueprint $tableBlueprint
+     */
+    public function fromTableBlueprint(TableBlueprint $tableBlueprint)
+    {
+        $classDefinition = $this->generateClassDefinition($tableBlueprint);
+        $abstractClassDefinition = $this->generateAbstractClassDefinition($tableBlueprint);
+
+        /*
+         * TODO: Add generic methods like "get by id"
+         */
+
+        /*
+         * Write the Class Files
+         */
+        $this->writeClassFiles($classDefinition, $abstractClassDefinition);
+    }
+
+    public function generateClassDefinition(TableBlueprint $tableBlueprint): ClassDefinition
+    {
+        $abstractClassDefinition = $this->generateAbstractClassDefinition($tableBlueprint);
+
+        $className = $this->getClassName($tableBlueprint);
+        $namespace = $this->getClassNamespace($tableBlueprint);
+        $classDefinition = new ClassDefinition($className, $namespace);
+        $classDefinition->setParentClass($abstractClassDefinition->getFullyQualifiedName());
+
+        return $classDefinition;
+    }
+
+    public function generateAbstractClassDefinition(TableBlueprint $tableBlueprint): ClassDefinition
+    {
+        $abstractClassName = $this->getAbstractClassName($tableBlueprint);
+        $abstractNamespace = $this->getAbstractClassNamespace($tableBlueprint);
+        $abstractClassDefinition = new ClassDefinition($abstractClassName,
+            $abstractNamespace,
+            AbstractEnum::abstractEnum());
+
+        $modelObjectTypeDefinition
+            = new ObjectTypeDefinition($this->modelGenerator->getFullyQualifiedClassName($tableBlueprint));
+
+        $abstractClassDefinition
+            # include the WithDataMap trait
+            ->addTrait(new ClassTraitDefinition($this->modelDataMapGenerator->generateModelDataMapAccessorTrait($this->modelDataMapGenerator->getFullyQualifiedClassName($tableBlueprint))
+                ->getFullyQualifiedName()))
+            ->addConstant(new ClassConstantDefinition($this->getMapFromFailedConstantName($modelObjectTypeDefinition),
+                $this->getMapFromFailedConstantName($modelObjectTypeDefinition),
+                VisibilityEnum::protectedEnum()),)
+            ->addConstant(new ClassConstantDefinition($this->getMapToFailedConstantName($modelObjectTypeDefinition),
+                $this->getMapToFailedConstantName($modelObjectTypeDefinition),
+                VisibilityEnum::protectedEnum()))
+        ;
+
+        $this->addFetchByUniqueColumnMethods($tableBlueprint, $abstractClassDefinition);
+
+        $abstractClassDefinition->addMethodDefinition($this->generateCreateMethod($tableBlueprint,
+            $abstractClassDefinition));
+
+        $abstractClassDefinition->addMethodDefinition($this->generateUpdateMethod($tableBlueprint,
+            $abstractClassDefinition));
+
+        return $abstractClassDefinition;
+    }
+
+    /**
+     * @param TableBlueprint $tableBlueprint
+     *
+     * @return string
+     */
+    public function getFullyQualifiedClassName(TableBlueprint $tableBlueprint): string
+    {
+        return $this->getClassNamespace($tableBlueprint) . '\\' . $this->getClassName($tableBlueprint);
+    }
+
+    public function getClassNamespace(TableBlueprint $tableBlueprint): string
+    {
+        return $this->dataAccessGeneratorConfiguration->getNamespace();
+    }
+
+    public function getClassName(TableBlueprint $tableBlueprint): string
+    {
+        return ClassNameTool::snakeCaseToClassName($this->dataAccessGeneratorConfiguration->getClassPrefix(),
+            $tableBlueprint->getName(),
+            $this->dataAccessGeneratorConfiguration->getClassSuffix());
+    }
+
+    public function getFetchByUniqueColumnFunctionName(ColumnBlueprint $uniqueColumn): string
+    {
+        return "fetchBy" . Str::studly($uniqueColumn->getColumnName());
+    }
+
+    private function generateCreateMethod(TableBlueprint $tableBlueprint,
+        ClassDefinition $classDefinition): ClassMethodDefinition
+    {
+        $modelObjectTypeDefinition
+            = new ObjectTypeDefinition($this->modelGenerator->getFullyQualifiedClassName($tableBlueprint));
+        $mySqlErrorTypesObjectTypeDefinition = new ObjectTypeDefinition('\app\DataStores\MySql\MySqlErrorTypes');
+        $queryExceptionTypeDefinition = new ObjectTypeDefinition('\Illuminate\Database\QueryException');
+        $logMessageObjectTypeDefinition = new ObjectTypeDefinition('\app\Patterns\Log\LogMessage');
+        $logExceptionObjectTypeDefinition = new ObjectTypeDefinition('\app\Patterns\Log\LogException');
+        $returnTypeObjectTypeDefinition
+            = new ObjectTypeDefinition('\app\Patterns\MethodResponses\CreateMethodResponse');
+        $dtoTypeObjectTypeDefinition
+            = new ObjectTypeDefinition($this->dataTransportObjectGenerator->getFullyQualifiedClassName($tableBlueprint));
+        $modelTypeObjectTypeDefinition
+            = new ObjectTypeDefinition($this->modelGenerator->getFullyQualifiedClassName($tableBlueprint));
+        $modelDataMapTraitObjectTypeDefinition
+            = new ObjectTypeDefinition($this->modelDataMapGenerator->generateModelDataMapAccessorTrait($this->modelDataMapGenerator->getFullyQualifiedClassName($tableBlueprint))
+            ->getFullyQualifiedName());
+        $classDefinition->addImport($modelObjectTypeDefinition)
+            ->addImport($mySqlErrorTypesObjectTypeDefinition)
+            ->addImport($logMessageObjectTypeDefinition)
+            ->addImport($logExceptionObjectTypeDefinition)
+            ->addImport($returnTypeObjectTypeDefinition)
+            ->addImport($dtoTypeObjectTypeDefinition)
+            ->addImport($modelTypeObjectTypeDefinition)
+            ->addImport($modelDataMapTraitObjectTypeDefinition)
+        ;
+
+        $functionName = "create";
+        $returnType = PhpTypeEnum::objectOfType($returnTypeObjectTypeDefinition->getFullyQualifiedName());
+        $modelVariableName = $this->modelGenerator->getClassAsVariableName($tableBlueprint);
+
+        $methodFailedConstantName = sprintf('%s_CREATE_FAILED',
+            Str::upper($modelTypeObjectTypeDefinition->getImportableName()));
+        $classDefinition->addConstant(new ClassConstantDefinition($methodFailedConstantName,
+            $methodFailedConstantName,
+            VisibilityEnum::protectedEnum()));
+        $dtoParameterDefinition = $this->getDtoFunctionParameterDefinition($dtoTypeObjectTypeDefinition);
+        $exceptionVariableName = 'exception';
+
+        /*
+         * Build Unique Constraint Error Handling Conditions
+         */
+        $catchBlockStatements = new StatementDefinitionCollection();
+        $catchBlockStatements->addStatementDefinition((new CommentBlockStatementDefinition())->addLine("Treat unique key errors as validation failures"))
+            ->addStatementDefinition((new StatementBlockDefinition(new RawStatementDefinition(sprintf("if (!empty(\$%s->errorInfo[1]) && %s::UNIQUE_KEY_VIOLATION_TYPE_ID === \$%s->errorInfo[1])",
+                $exceptionVariableName,
+                $mySqlErrorTypesObjectTypeDefinition->getImportableName(),
+                $exceptionVariableName))))->addStatementDefinition($foreachBlock
+                = (new StatementBlockDefinition(new RawStatementDefinition(sprintf("foreach (\$%s->errorInfo as \$value)",
+                $exceptionVariableName,))))))
+        ;
+
+        foreach ($tableBlueprint->getUniqueIndexes(false) as $uniqueIndexBlueprint) {
+            $validationMessageMethod = $this->getUniqueKeyViolationValidationMessageMethod($classDefinition,
+                $uniqueIndexBlueprint);
+
+            $classDefinition->addMethodDefinition($validationMessageMethod);
+
+            $foreachBlock->addStatementDefinition((new StatementBlockDefinition(new RawStatementDefinition(sprintf("if (str_contains(\$value, '%s'))",
+                $uniqueIndexBlueprint->getName()))))->addStatementDefinition(new RawStatementDefinition(sprintf("return %s::invalid([\$this->%s()]);",
+                $returnTypeObjectTypeDefinition->getImportableName(),
+                $validationMessageMethod->getFunctionName()))));
+        }
+
+        $catchBlockStatements->addStatementDefinition(new RawStatementDefinition(sprintf("return %s::error([new %s(static::%s), new %s(\$%s),]);",
+            $returnTypeObjectTypeDefinition->getImportableName(),
+            $logMessageObjectTypeDefinition->getImportableName(),
+            $methodFailedConstantName,
+            $logExceptionObjectTypeDefinition->getImportableName(),
+            $exceptionVariableName)));
+        /*
+         * Build Class Method definition
+         */
+        $classMethodDefinition = new ClassMethodDefinition($functionName, $returnType, [$dtoParameterDefinition]);
+        $classMethodDefinition->appendBodyStatement(// new model statement
+            new RawStatementDefinition(sprintf("\$%s = new %s();",
+                $modelVariableName,
+                $modelTypeObjectTypeDefinition->getImportableName())))
+            ->appendBodyStatement((new StatementBlockDefinition(new RawStatementDefinition(sprintf("if (!\$this->%s()->from%s(\$%s, \$%s))",
+                $this->modelDataMapGenerator->getModelMapAccessorTraitMethodName($tableBlueprint),
+                $dtoTypeObjectTypeDefinition->getImportableName(),
+                $modelVariableName,
+                $dtoParameterDefinition->getParameterName()))))->addStatementDefinition(new RawStatementDefinition(sprintf("return %s::error([new %s(static::%s), new %s(static::%s)]);",
+                $returnTypeObjectTypeDefinition->getImportableName(),
+                $logMessageObjectTypeDefinition->getImportableName(),
+                $this->getMapFromFailedConstantName($modelTypeObjectTypeDefinition),
+                $logMessageObjectTypeDefinition->getImportableName(),
+                $methodFailedConstantName))))
+            // try save block
+            ->appendBodyStatement(
+                $this->getSaveModelTryBlock($modelVariableName,
+                $returnTypeObjectTypeDefinition,
+                $logMessageObjectTypeDefinition,
+                $methodFailedConstantName,
+                $queryExceptionTypeDefinition,
+                $exceptionVariableName,
+                $catchBlockStatements))
+            // after try block, map model to dto
+            ->appendBodyStatement($this->getMapToDtoStatementBlock($tableBlueprint,
+                $dtoTypeObjectTypeDefinition,
+                $modelVariableName,
+                $dtoParameterDefinition,
+                $this->getMapToFailedConstantName($modelObjectTypeDefinition),
+                $returnTypeObjectTypeDefinition))
+            # return created
+            ->appendBodyStatement(new RawStatementDefinition(sprintf("return %s::created();",
+                $returnTypeObjectTypeDefinition->getImportableName())))
+        ;
+
+        return $classMethodDefinition;
+    }
+
+    private function generateUpdateMethod(TableBlueprint $tableBlueprint,
+        ClassDefinition $classDefinition): ClassMethodDefinition
+    {
+        $modelObjectTypeDefinition
+            = new ObjectTypeDefinition($this->modelGenerator->getFullyQualifiedClassName($tableBlueprint));
+        $mySqlErrorTypesObjectTypeDefinition = new ObjectTypeDefinition('\app\DataStores\MySql\MySqlErrorTypes');
+        $queryExceptionTypeDefinition = new ObjectTypeDefinition('\Illuminate\Database\QueryException');
+        $logMessageObjectTypeDefinition = new ObjectTypeDefinition('\app\Patterns\Log\LogMessage');
+        $logExceptionObjectTypeDefinition = new ObjectTypeDefinition('\app\Patterns\Log\LogException');
+        $returnTypeObjectTypeDefinition
+            = new ObjectTypeDefinition('\app\Patterns\MethodResponses\UpdateMethodResponse');
+        $dtoTypeObjectTypeDefinition
+            = new ObjectTypeDefinition($this->dataTransportObjectGenerator->getFullyQualifiedClassName($tableBlueprint));
+        $modelTypeObjectTypeDefinition
+            = new ObjectTypeDefinition($this->modelGenerator->getFullyQualifiedClassName($tableBlueprint));
+        $modelDataMapTraitObjectTypeDefinition
+            = new ObjectTypeDefinition($this->modelDataMapGenerator->generateModelDataMapAccessorTrait($this->modelDataMapGenerator->getFullyQualifiedClassName($tableBlueprint))
+            ->getFullyQualifiedName());
+        $classDefinition->addImport($modelObjectTypeDefinition)
+            ->addImport($mySqlErrorTypesObjectTypeDefinition)
+            ->addImport($logMessageObjectTypeDefinition)
+            ->addImport($logExceptionObjectTypeDefinition)
+            ->addImport($returnTypeObjectTypeDefinition)
+            ->addImport($dtoTypeObjectTypeDefinition)
+            ->addImport($modelTypeObjectTypeDefinition)
+            ->addImport($modelDataMapTraitObjectTypeDefinition)
+        ;
+
+        $functionName = "update";
+        $returnType = PhpTypeEnum::objectOfType($returnTypeObjectTypeDefinition->getFullyQualifiedName());
+        $modelVariableName = $this->modelGenerator->getClassAsVariableName($tableBlueprint);
+
+        $methodFailedConstantName = sprintf('%s_UPDATE_FAILED',
+            Str::upper($modelTypeObjectTypeDefinition->getImportableName()));
+        $classDefinition->addConstant(new ClassConstantDefinition($methodFailedConstantName,
+            $methodFailedConstantName,
+            VisibilityEnum::protectedEnum()));
+        $dtoParameterDefinition = $this->getDtoFunctionParameterDefinition($dtoTypeObjectTypeDefinition);
+        $exceptionVariableName = 'exception';
+
+        /*
+         * Build Unique Constraint Error Handling Conditions
+         */
+        $catchBlockStatements = new StatementDefinitionCollection();
+        $catchBlockStatements->addStatementDefinition((new CommentBlockStatementDefinition())->addLine("Treat unique key errors as validation failures"))
+            ->addStatementDefinition((new StatementBlockDefinition(new RawStatementDefinition(sprintf("if (!empty(\$%s->errorInfo[1]) && %s::UNIQUE_KEY_VIOLATION_TYPE_ID === \$%s->errorInfo[1])",
+                $exceptionVariableName,
+                $mySqlErrorTypesObjectTypeDefinition->getImportableName(),
+                $exceptionVariableName))))->addStatementDefinition($foreachBlock
+                = (new StatementBlockDefinition(new RawStatementDefinition(sprintf("foreach (\$%s->errorInfo as \$value)",
+                $exceptionVariableName,))))))
+        ;
+
+        foreach ($tableBlueprint->getUniqueIndexes(false) as $uniqueIndexBlueprint) {
+            $validationMessageMethod = $this->getUniqueKeyViolationValidationMessageMethod($classDefinition,
+                $uniqueIndexBlueprint);
+
+            $classDefinition->addMethodDefinition($validationMessageMethod);
+
+            $foreachBlock->addStatementDefinition((new StatementBlockDefinition(new RawStatementDefinition(sprintf("if (str_contains(\$value, '%s'))",
+                $uniqueIndexBlueprint->getName()))))->addStatementDefinition(new RawStatementDefinition(sprintf("return %s::invalid([\$this->%s()]);",
+                $returnTypeObjectTypeDefinition->getImportableName(),
+                $validationMessageMethod->getFunctionName()))));
+        }
+
+        $catchBlockStatements->addStatementDefinition(new RawStatementDefinition(sprintf("return %s::error([new %s(static::%s), new %s(\$%s),]);",
+            $returnTypeObjectTypeDefinition->getImportableName(),
+            $logMessageObjectTypeDefinition->getImportableName(),
+            $methodFailedConstantName,
+            $logExceptionObjectTypeDefinition->getImportableName(),
+            $exceptionVariableName)));
+        /*
+         * Define Find Model by Dto Id try block
+         */
+        $findModelTryBlock = new TryBlockDefinition();
+        $findModelTryBlock->addStatementDefinition(// find model statement
+            new RawStatementDefinition(sprintf("\$%s = %s::find(\$%s->getId());",
+                $modelVariableName,
+                $modelTypeObjectTypeDefinition->getImportableName(),
+                $dtoParameterDefinition->getParameterName())))
+            ->addCatchStatements(PhpTypeEnum::objectOfType(\Exception::class),
+                $exceptionVariableName,
+                (new StatementDefinitionCollection())->addStatementDefinition(new RawStatementDefinition(sprintf("return %s::error([new LogMessage(static::%s), new LogException(\$%s),]);",
+                    $returnTypeObjectTypeDefinition->getImportableName(),
+                    $methodFailedConstantName,
+                    $exceptionVariableName))))
+        ;
+        /*
+         * Define if model->save() Try Block
+         */
+        $modelSaveTryBlock = new TryBlockDefinition();
+        $modelSaveTryBlock
+            ->addStatementDefinition(
+                (new StatementBlockDefinition(
+                    new RawStatementDefinition(
+                        sprintf(
+                            "if (!\$%s->save())",
+                            $modelVariableName))))
+                ->addStatementDefinition(
+                    new RawStatementDefinition(
+                        sprintf(
+                            "return %s::error([new %s(static::%s)]);",
+                            $returnTypeObjectTypeDefinition->getImportableName(),
+                            $logMessageObjectTypeDefinition->getImportableName(),
+                            $methodFailedConstantName
+                        )))
+            );
+            // if (!$model->save())
+        /*
+         * Build Class Method definition
+         */
+        $classMethodDefinition = new ClassMethodDefinition($functionName, $returnType, [$dtoParameterDefinition]);
+        $classMethodDefinition
+            // Add try find model
+            ->appendBodyStatement($findModelTryBlock)
+            // Add if model not found
+            ->appendBodyStatement($this->getModelNotFoundStatementBlock($modelVariableName,
+                $returnTypeObjectTypeDefinition))
+            // Add map DTO to Model block
+            ->appendBodyStatement($this->getMapFromDtoStatementBlock($tableBlueprint,
+                $dtoTypeObjectTypeDefinition,
+                $modelVariableName,
+                $dtoParameterDefinition,
+                $returnTypeObjectTypeDefinition,
+                $logMessageObjectTypeDefinition,
+                $modelTypeObjectTypeDefinition,
+                $methodFailedConstantName))
+            // try save block
+            ->appendBodyStatement(
+                $this->getSaveModelTryBlock($modelVariableName,
+                    $returnTypeObjectTypeDefinition,
+                    $logMessageObjectTypeDefinition,
+                    $methodFailedConstantName,
+                    $queryExceptionTypeDefinition,
+                    $exceptionVariableName,
+                    $catchBlockStatements))
+            // after try block, map model to dto
+            ->appendBodyStatement($this->getMapToDtoStatementBlock($tableBlueprint,
+                $dtoTypeObjectTypeDefinition,
+                $modelVariableName,
+                $dtoParameterDefinition,
+                $this->getMapToFailedConstantName($modelObjectTypeDefinition),
+                $returnTypeObjectTypeDefinition))
+            # return updated
+            ->appendBodyStatement(new RawStatementDefinition(sprintf("return %s::updated();",
+                $returnTypeObjectTypeDefinition->getImportableName())))
+        ;
+
+        return $classMethodDefinition;
+    }
+
+    /**
+     * Calls \Reliese\Generator\DataAccess\DataAccessGenerator::generateFetchByUniqueColumnMethodDefinition foreach
+     * unique column
+     *
+     * @param TableBlueprint  $tableBlueprint
+     * @param ClassDefinition $abstractClassDefinition
+     *
+     * @return array
+     */
+    private function addFetchByUniqueColumnMethods(TableBlueprint $tableBlueprint,
+        ClassDefinition $abstractClassDefinition)
+    {
+        $uniqueColumnGroups = $tableBlueprint->getUniqueColumnGroups();
+
+        foreach ($uniqueColumnGroups as $uniqueColumnGroup) {
+            if (1 < count($uniqueColumnGroup)) {
+                continue;
+            }
+
+            $uniqueColumn = array_pop($uniqueColumnGroup);
+
+            $abstractClassDefinition->addMethodDefinition($this->generateFetchByUniqueColumnMethodDefinition($abstractClassDefinition,
+                $tableBlueprint,
+                $uniqueColumn));
+        }
+    }
+
+    /**
+     * Example Output
+     * 
+     * public function fetchByExternalKey(AccountDto $accountDto): FetchMethodResponse
+     * {
+     *     try {
+     *         $account = Account::where(Account::EXTERNAL_KEY, $accountDto->getExternalKey())->first();
+     *     } catch (\Exception $exception) {
+     *         return FetchMethodResponse::error([new LogMessage(static::ACCOUNT_FETCH_BY_EXTERNAL_KEY_FAILED), new
+     * LogException($exception),]);
+     *     }
+     *     if (!$account) {
+     *         return FetchMethodResponse::notFound();
+     *     }
+     *     if (!$this->getAccountMap()->toAccountDto($account, $accountDto)) {
+     *         return FetchMethodResponse::error([new LogMessage(self::ACCOUNT_MAP_TO_DTO_FAILED)]);
+     *     }
+     *     return FetchMethodResponse::found();
+     * }
+     * 
+     *
+     * @param ClassDefinition $classDefinition
+     * @param TableBlueprint  $tableBlueprint
+     * @param ColumnBlueprint $uniqueColumn
+     *
+     * @return ClassMethodDefinition
+     */
+    private function generateFetchByUniqueColumnMethodDefinition(ClassDefinition $classDefinition,
+        TableBlueprint $tableBlueprint,
+        ColumnBlueprint $uniqueColumn): ClassMethodDefinition
+    {
+        $logMessageObjectTypeDefinition = new ObjectTypeDefinition('\app\Patterns\Log\LogMessage');
+        $logExceptionObjectTypeDefinition = new ObjectTypeDefinition('\app\Patterns\Log\LogException');
+        $returnTypeObjectTypeDefinition = new ObjectTypeDefinition('\app\Patterns\MethodResponses\FetchMethodResponse');
+        $dtoTypeObjectTypeDefinition
+            = new ObjectTypeDefinition($this->dataTransportObjectGenerator->getFullyQualifiedClassName($tableBlueprint));
+        $modelObjectTypeDefinition
+            = new ObjectTypeDefinition($this->modelGenerator->getFullyQualifiedClassName($tableBlueprint));
+        $modelDataMapTraitObjectTypeDefinition
+            = new ObjectTypeDefinition($this->modelDataMapGenerator->generateModelDataMapAccessorTrait($this->modelDataMapGenerator->getFullyQualifiedClassName($tableBlueprint))
+            ->getFullyQualifiedName());
+        $classDefinition->addImport($logMessageObjectTypeDefinition)
+            ->addImport($logExceptionObjectTypeDefinition)
+            ->addImport($returnTypeObjectTypeDefinition)
+            ->addImport($dtoTypeObjectTypeDefinition)
+            ->addImport($modelObjectTypeDefinition)
+            ->addImport($modelDataMapTraitObjectTypeDefinition)
+        ;
+
+        $columnCamelName = Str::studly($uniqueColumn->getColumnName());
+
+        $modelVariableName = $this->modelGenerator->getClassAsVariableName($tableBlueprint);
+
+        $dtoParameterDefinition = $this->getDtoFunctionParameterDefinition($dtoTypeObjectTypeDefinition);
+
+        $classMethodDefinition = new ClassMethodDefinition($this->getFetchByUniqueColumnFunctionName($uniqueColumn),
+            PhpTypeEnum::objectOfType($returnTypeObjectTypeDefinition->getFullyQualifiedName()),
+            [$dtoParameterDefinition]);
+
+        $methodFailedConstantName = sprintf('%s_FETCH_BY_%s_FAILED',
+            Str::upper($modelObjectTypeDefinition->getImportableName()),
+            Str::upper(Str::snake($columnCamelName)));
+
+        $classDefinition
+            # include _FETCH_BY__FAILED constant
+            ->addConstant(new ClassConstantDefinition($methodFailedConstantName,
+                $methodFailedConstantName,
+                VisibilityEnum::protectedEnum()));
+
+        $exceptionVariableName = 'exception';
+
+        $columnConstantDefinition = $this->modelGenerator->generateColumnConstantDefinition($uniqueColumn);
+        $classMethodDefinition->appendBodyStatement((new TryBlockDefinition())->addStatementDefinition(# include the Eloquent query to fetch the model by the unique column value
+            new RawStatementDefinition(sprintf("\$%s = %s::where(%s::%s, \$%s->get%s())->first();",
+                $modelVariableName,
+                $modelObjectTypeDefinition->getImportableName(),
+                $modelObjectTypeDefinition->getImportableName(),
+                $columnConstantDefinition->getName(),
+                $dtoParameterDefinition->getParameterName(),
+                $columnCamelName)))
+            ->addCatchStatements(PhpTypeEnum::objectOfType(\Exception::class),
+                $exceptionVariableName,
+                (new StatementDefinitionCollection())->addStatementDefinition(new RawStatementDefinition(sprintf("return FetchMethodResponse::error([new LogMessage(static::%s), new LogException(\$%s),]);",
+                    $methodFailedConstantName,
+                    $exceptionVariableName)))))
+            # include the conditional check to determine if it was found
+            ->appendBodyStatement($this->getModelNotFoundStatementBlock($modelVariableName,
+                $returnTypeObjectTypeDefinition))
+            # include the conditional check to determine if it was mapped successfully
+            ->appendBodyStatement($this->getMapToDtoStatementBlock($tableBlueprint,
+                $dtoTypeObjectTypeDefinition,
+                $modelVariableName,
+                $dtoParameterDefinition,
+                $this->getMapToFailedConstantName($modelObjectTypeDefinition),
+                $returnTypeObjectTypeDefinition))
+            ->appendBodyStatement(new RawStatementDefinition("return FetchMethodResponse::found();"))
+        ;
+
+        return $classMethodDefinition;
+    }
+
+    private function getAbstractClassName(TableBlueprint $tableBlueprint): string
+    {
+        return $this->dataAccessGeneratorConfiguration->getParentClassPrefix() . $this->getClassName($tableBlueprint);
+    }
+
+    private function getAbstractClassNamespace(TableBlueprint $tableBlueprint): string
+    {
+        return $this->getClassNamespace($tableBlueprint) . '\\Generated';
+    }
+
+    /**
+     * @param ClassDefinition $classDefinition
+     * @param ClassDefinition $abstractClassDefinition
+     */
+    private function writeClassFiles(ClassDefinition $classDefinition,
+        ClassDefinition $abstractClassDefinition,): void
+    {
+        $classFormatter = new ClassFormatter();
+
+        $dtoClassPhpCode = $classFormatter->format($classDefinition);
+        $abstractDtoPhpCode = $classFormatter->format($abstractClassDefinition);
+        //        echo "\n---Class---\n$dtoClassPhpCode\n\n\n---Base Class---\n$abstractDtoPhpCode\n\n";
+
+        $dtoClassFolder = $this->dataAccessGeneratorConfiguration->getPath();
+        $abstractDtoClassFolder = $dtoClassFolder . DIRECTORY_SEPARATOR . 'Generated';
+        if (!is_dir($dtoClassFolder)) {
+            \mkdir($dtoClassFolder, 0755, true);
+        }
+        if (!is_dir($abstractDtoClassFolder)) {
+            \mkdir($abstractDtoClassFolder, 0755, true);
+        }
+
+        $dtoFilePath = $dtoClassFolder . DIRECTORY_SEPARATOR . $classDefinition->getName() . '.php';
+        $abstractDtoFilePath = $abstractDtoClassFolder
+            . DIRECTORY_SEPARATOR
+            . $abstractClassDefinition->getName()
+            . '.php';
+
+        if (!\file_exists($dtoFilePath)) {
+            \file_put_contents($dtoFilePath, $dtoClassPhpCode);
+        }
+        \file_put_contents($abstractDtoFilePath, $abstractDtoPhpCode);
+    }
+
+    private function getMapToFailedConstantName(ObjectTypeDefinition $modelObjectTypeDefinition)
+    {
+        return sprintf('%s_MAP_TO_DTO_FAILED',
+            Str::upper($modelObjectTypeDefinition->getImportableName()));
+    }
+
+    private function getMapFromFailedConstantName(ObjectTypeDefinition $modelObjectTypeDefinition)
+    {
+        return sprintf('%s_MAP_FROM_DTO_FAILED',
+            Str::upper($modelObjectTypeDefinition->getImportableName()));
+    }
+
+    private function getDtoFunctionParameterDefinition(ObjectTypeDefinition $dtoTypeObjectTypeDefinition): FunctionParameterDefinition
+    {
+        return new FunctionParameterDefinition($this->getDtoVariableName($dtoTypeObjectTypeDefinition),
+            PhpTypeEnum::objectOfType($dtoTypeObjectTypeDefinition->getFullyQualifiedName()));
+    }
+
+    private function getDtoVariableName(ObjectTypeDefinition $dtoTypeObjectTypeDefinition): string
+    {
+        $name = $dtoTypeObjectTypeDefinition->getImportableName();
+        return strtolower($name[0]) . substr($name, 1);
+    }
+
+    private function getUniqueKeyViolationValidationMessageMethod(ClassDefinition $classDefinition,
+        IndexBlueprint $indexBlueprint): ClassMethodDefinition
+    {
+        $returnType = new ObjectTypeDefinition(\Symfony\Component\Translation\TranslatableMessage::class);
+        $classDefinition->addImport($returnType);
+
+        $functionName = sprintf("getUniqueKeyValidationMessageFor%s",
+            Str::studly($indexBlueprint->getName()));
+
+        return new ClassMethodDefinition($functionName,
+            PhpTypeEnum::objectOfType($returnType->getFullyQualifiedName()),
+            [],
+            VisibilityEnum::protectedEnum(),
+            InstanceEnum::instanceEnum(),
+            AbstractEnum::abstractEnum());
+    }
+
+    /**
+     * @param TableBlueprint              $tableBlueprint
+     * @param ObjectTypeDefinition        $dtoTypeObjectTypeDefinition
+     * @param string                      $modelVariableName
+     * @param FunctionParameterDefinition $dtoParameterDefinition
+     * @param string                      $mapToFailedConstantName
+     * @param ObjectTypeDefinition        $returnObjectTypeDefinition
+     *
+     * @return StatementBlockDefinition
+     */
+    private function getMapToDtoStatementBlock(TableBlueprint $tableBlueprint,
+        ObjectTypeDefinition $dtoTypeObjectTypeDefinition,
+        string $modelVariableName,
+        FunctionParameterDefinition $dtoParameterDefinition,
+        string $mapToFailedConstantName,
+        ObjectTypeDefinition $returnObjectTypeDefinition): StatementBlockDefinition
+    {
+        return (new StatementBlockDefinition(new RawStatementDefinition(sprintf(//    "if (!$this->accountMap->toAccountDto($accountModel, $accountDto))"
+            "if (!\$this->%s()->to%s(\$%s, \$%s))",
+            $this->modelDataMapGenerator->getModelMapAccessorTraitMethodName($tableBlueprint),
+            $dtoTypeObjectTypeDefinition->getImportableName(),
+            $modelVariableName,
+            $dtoParameterDefinition->getParameterName()))))->addStatementDefinition(new RawStatementDefinition(sprintf("return %s::error([new LogMessage(self::%s)]);",
+            $returnObjectTypeDefinition->getImportableName(),
+            $mapToFailedConstantName)));
+    }
+
+    /**
+     * Example statement block output
+     * 
+     * if (!$this->getPersonMap()->fromPersonDto($person, $personDto)) {
+     *   return UpdateMethodResponse::error(
+     *     [new LogMessage(static::PERSON_MAP_FROM_DTO_FAILED), new LogMessage(static::PERSON_UPDATE_FAILED)]);
+     * }
+     * 
+     *
+     * @param TableBlueprint              $tableBlueprint
+     * @param ObjectTypeDefinition        $dtoTypeObjectTypeDefinition
+     * @param string                      $modelVariableName
+     * @param FunctionParameterDefinition $dtoParameterDefinition
+     * @param ObjectTypeDefinition        $returnTypeObjectTypeDefinition
+     * @param ObjectTypeDefinition        $logMessageObjectTypeDefinition
+     * @param ObjectTypeDefinition        $modelTypeObjectTypeDefinition
+     * @param string                      $methodFailedConstantName
+     *
+     * @return StatementBlockDefinition
+     */
+    private function getMapFromDtoStatementBlock(TableBlueprint $tableBlueprint,
+        ObjectTypeDefinition $dtoTypeObjectTypeDefinition,
+        string $modelVariableName,
+        FunctionParameterDefinition $dtoParameterDefinition,
+        ObjectTypeDefinition $returnTypeObjectTypeDefinition,
+        ObjectTypeDefinition $logMessageObjectTypeDefinition,
+        ObjectTypeDefinition $modelTypeObjectTypeDefinition,
+        string $methodFailedConstantName): StatementBlockDefinition
+    {
+        return (new StatementBlockDefinition(new RawStatementDefinition(sprintf("if (!\$this->%s()->from%s(\$%s, \$%s))",
+            $this->modelDataMapGenerator->getModelMapAccessorTraitMethodName($tableBlueprint),
+            $dtoTypeObjectTypeDefinition->getImportableName(),
+            $modelVariableName,
+            $dtoParameterDefinition->getParameterName()))))->addStatementDefinition(new RawStatementDefinition(sprintf("return %s::error([new %s(static::%s), new %s(static::%s)]);",
+            $returnTypeObjectTypeDefinition->getImportableName(),
+            $logMessageObjectTypeDefinition->getImportableName(),
+            $this->getMapFromFailedConstantName($modelTypeObjectTypeDefinition),
+            $logMessageObjectTypeDefinition->getImportableName(),
+            $methodFailedConstantName)));
+    }
+
+    /**
+     * @param string               $modelVariableName
+     * @param ObjectTypeDefinition $returnTypeObjectTypeDefinition
+     *
+     * @return StatementBlockDefinition
+     */
+    private function getModelNotFoundStatementBlock(string $modelVariableName,
+        ObjectTypeDefinition $returnTypeObjectTypeDefinition): StatementBlockDefinition
+    {
+        return (new StatementBlockDefinition(new RawStatementDefinition(sprintf("if (!\$%s)",
+            $modelVariableName))))->addStatementDefinition(new RawStatementDefinition(sprintf("return %s::notFound();",
+            $returnTypeObjectTypeDefinition->getImportableName())));
+    }
+
+    /**
+     * @param string                        $modelVariableName
+     * @param ObjectTypeDefinition          $returnTypeObjectTypeDefinition
+     * @param ObjectTypeDefinition          $logMessageObjectTypeDefinition
+     * @param string                        $methodFailedConstantName
+     * @param ObjectTypeDefinition          $queryExceptionTypeDefinition
+     * @param string                        $exceptionVariableName
+     * @param StatementDefinitionCollection $catchBlockStatements
+     *
+     * @return TryBlockDefinition
+     */
+    private function getSaveModelTryBlock(string $modelVariableName,
+        ObjectTypeDefinition $returnTypeObjectTypeDefinition,
+        ObjectTypeDefinition $logMessageObjectTypeDefinition,
+        string $methodFailedConstantName,
+        ObjectTypeDefinition $queryExceptionTypeDefinition,
+        string $exceptionVariableName,
+        StatementDefinitionCollection $catchBlockStatements): TryBlockDefinition
+    {
+        return (new TryBlockDefinition())
+            // if (!$model->save()
+            ->addStatementDefinition((new StatementBlockDefinition(new RawStatementDefinition(sprintf("if (!\$%s->save())",
+                $modelVariableName))))->addStatementDefinition(new RawStatementDefinition(sprintf("return %s::error([new %s(static::%s)]);",
+                $returnTypeObjectTypeDefinition->getImportableName(),
+                $logMessageObjectTypeDefinition->getImportableName(),
+                $methodFailedConstantName))))
+            // begin catch block
+            ->addCatchStatements(PhpTypeEnum::objectOfType($queryExceptionTypeDefinition->getFullyQualifiedName()),
+                $exceptionVariableName,
+                $catchBlockStatements)
+            ;
+}
+}
diff --git a/src/Generator/DataAttribute/DataAttributeGenerator.php b/src/Generator/DataAttribute/DataAttributeGenerator.php
new file mode 100644
index 00000000..75256080
--- /dev/null
+++ b/src/Generator/DataAttribute/DataAttributeGenerator.php
@@ -0,0 +1,155 @@
+dataAttributeGeneratorConfiguration = $relieseConfiguration->getDataAttributeGeneratorConfiguration();
+        /*
+         * TODO: inject a MySql / Postgress or other DataType mapping as needed
+         */
+        $this->dataTypeMap = new MySqlDataTypeMap();
+    }
+
+    /**
+     * @param TableBlueprint $tableBlueprint
+     */
+    public function fromColumnBlueprint(TableBlueprint $tableBlueprint)
+    {
+        foreach ($tableBlueprint->getColumnBlueprints() as $columnBlueprint) {
+
+            $traitName = $this->getTraitName($tableBlueprint, $columnBlueprint);
+
+            $namespace = $this->getTraitNamespace($tableBlueprint, $columnBlueprint);
+
+            $traitDefinition = new TraitDefinition($traitName, $namespace);
+
+            $traitDefinition
+                ->addClassComment(
+                    sprintf("Generated from table column %s.%s", $tableBlueprint->getName(), $columnBlueprint->getColumnName())
+                )
+                ->addClassComment(
+                "This file is only generated if it does not already exist. To regenerate, remove this file."
+                )
+            ;
+
+            $propertyName = ClassNameTool::columnNameToPropertyName($columnBlueprint->getColumnName());
+
+            $phpTypeEnum = $this->dataTypeMap->getPhpTypeEnumFromDatabaseType(
+                $columnBlueprint->getDataType(),
+                $columnBlueprint->getMaximumCharacters(),
+                $columnBlueprint->getNumericPrecision(),
+                $columnBlueprint->getNumericScale(),
+                $columnBlueprint->getIsNullable()
+            );
+
+            $traitDefinition->addProperty(
+                (new ClassPropertyDefinition($propertyName, $phpTypeEnum))
+                ->withSetter()
+                ->withGetter()
+            );
+
+            /*
+             * Write the Class Files
+             */
+            $this->writeTraitFile($traitDefinition);
+        }
+    }
+
+    /**
+     * @param TableBlueprint $tableBlueprint
+     * @param ColumnBlueprint $columnBlueprint
+     *
+     * @return string
+     */
+    public function getFullyQualifiedTraitName(TableBlueprint $tableBlueprint, ColumnBlueprint $columnBlueprint): string
+    {
+        return $this->getTraitNamespace($tableBlueprint, $columnBlueprint).'\\'.$this->getTraitName($tableBlueprint, $columnBlueprint);
+    }
+
+    public function getTraitNamespace(TableBlueprint $tableBlueprint, ColumnBlueprint $columnBlueprint): string
+    {
+        $tableClassName = ClassNameTool::snakeCaseToClassName(null, $tableBlueprint->getName(), null);
+        return $this->dataAttributeGeneratorConfiguration->getNamespace().'\\'.$tableClassName;
+    }
+
+    public function getTraitName(
+        TableBlueprint $tableBlueprint,
+        ColumnBlueprint $columnBlueprint
+    ): string {
+        return ClassNameTool::snakeCaseToClassName(
+            $this->dataAttributeGeneratorConfiguration->getTraitPrefix(),
+            $columnBlueprint->getColumnName(),
+            $this->dataAttributeGeneratorConfiguration->getTraitSuffix(),
+        );
+    }
+
+    /**
+     * @param TraitDefinition $traitDefinition
+     */
+    private function writeTraitFile(
+        TraitDefinition $traitDefinition
+    ): void
+    {
+        $classFormatter = new ClassFormatter();
+
+        $traitPhpCode = $classFormatter->format($traitDefinition);
+//        echo "\n---Trait---\n$traitPhpCode\n\n";
+
+        $traitDirectory = $this->dataAttributeGeneratorConfiguration->getPath();
+        $namespaceParts = \explode('\\', $traitDefinition->getNamespace());
+        $traitDirectory .= '/'.\array_pop($namespaceParts);
+        echo "\n---Trait Path---\n$traitDirectory\n\n";
+
+        if (!is_dir($traitDirectory)) {
+            \mkdir($traitDirectory, 0755, true);
+        }
+
+        $traitFilePath = $traitDirectory . DIRECTORY_SEPARATOR . $traitDefinition->getName() . '.php';
+//        echo "\n---Trait File---\n$traitFilePath\n\n";
+        if (!\file_exists($traitFilePath)) {
+            \file_put_contents($traitFilePath, $traitPhpCode);
+        } else {
+            Log::warning("File already exists, skipped: $traitFilePath");
+        }
+    }
+}
diff --git a/src/Generator/DataMap/ModelDataMapGenerator.php b/src/Generator/DataMap/ModelDataMapGenerator.php
index 7f7dd0ae..cde2fe08 100644
--- a/src/Generator/DataMap/ModelDataMapGenerator.php
+++ b/src/Generator/DataMap/ModelDataMapGenerator.php
@@ -2,20 +2,33 @@
 
 namespace Reliese\Generator\DataMap;
 
+use app\DataTransport\Objects\PrimaryDatabase\OrganizationDto;
+use app\Services\Identity\AccountService;
 use Illuminate\Support\Str;
+use Reliese\Blueprint\ColumnBlueprint;
 use Reliese\Blueprint\DatabaseBlueprint;
 use Reliese\Blueprint\TableBlueprint;
+use Reliese\Configuration\DataTransportObjectGeneratorConfiguration;
 use Reliese\Configuration\ModelDataMapGeneratorConfiguration;
-use Reliese\Generator\DataTransport\DataTransportGenerator;
+use Reliese\Configuration\RelieseConfiguration;
+use Reliese\Generator\DataTransport\DataTransportObjectGenerator;
 use Reliese\Generator\Model\ModelGenerator;
 use Reliese\Generator\MySqlDataTypeMap;
 use Reliese\MetaCode\Definition\ClassDefinition;
 use Reliese\MetaCode\Definition\ClassMethodDefinition;
+use Reliese\MetaCode\Definition\ClassPropertyDefinition;
+use Reliese\MetaCode\Definition\ClassTraitDefinition;
 use Reliese\MetaCode\Definition\FunctionParameterDefinition;
 use Reliese\MetaCode\Definition\RawStatementDefinition;
+use Reliese\MetaCode\Definition\StatementBlockDefinition;
+use Reliese\MetaCode\Definition\StatementDefinitionCollection;
+use Reliese\MetaCode\Definition\TraitDefinition;
+use Reliese\MetaCode\Enum\InstanceEnum;
 use Reliese\MetaCode\Enum\PhpTypeEnum;
+use Reliese\MetaCode\Enum\VisibilityEnum;
 use Reliese\MetaCode\Format\ClassFormatter;
 use Reliese\MetaCode\Tool\ClassNameTool;
+use Reliese\MetaCode\Writers\CodeWriter;
 use function file_exists;
 use function file_put_contents;
 use function mkdir;
@@ -27,9 +40,24 @@
 class ModelDataMapGenerator
 {
     /**
-     * @var DataTransportGenerator
+     * @var DataTransportObjectGenerator
      */
-    private DataTransportGenerator $dataTransportGenerator;
+    private DataTransportObjectGenerator $dataTransportObjectGenerator;
+
+    /**
+     * @var DataTransportObjectGeneratorConfiguration
+     */
+    private DataTransportObjectGeneratorConfiguration $dataTransportObjectGeneratorConfiguration;
+
+    /**
+     * @var ClassDefinition[]
+     */
+    private array $generatedAbstractModelDataMapClassDefinitions = [];
+
+    /**
+     * @var ClassDefinition[]
+     */
+    private array $generatedModelDataMapClassDefinitions = [];
 
     /**
      * @var ModelDataMapGeneratorConfiguration
@@ -54,23 +82,19 @@ class ModelDataMapGenerator
     /**
      * ModelDataMapGenerator constructor.
      *
-     * @param ModelDataMapGeneratorConfiguration $modelDataMapGeneratorConfiguration
-     * @param ModelGenerator $modelGenerator
-     * @param DataTransportGenerator $dataTransportGenerator
+     * @param RelieseConfiguration $relieseConfiguration
      */
-    public function __construct(
-        ModelDataMapGeneratorConfiguration $modelDataMapGeneratorConfiguration,
-        ModelGenerator $modelGenerator,
-        DataTransportGenerator $dataTransportGenerator
-    ) {
-        $this->modelDataMapGeneratorConfiguration = $modelDataMapGeneratorConfiguration;
+    public function __construct(RelieseConfiguration $relieseConfiguration)
+    {
+        $this->modelDataMapGeneratorConfiguration = $relieseConfiguration->getModelDataMapGeneratorConfiguration();
         /*
          * TODO: inject a MySql / Postgress or other DataType mapping as needed
          */
         $this->dataTypeMap = new MySqlDataTypeMap();
 
-        $this->modelGenerator = $modelGenerator;
-        $this->dataTransportGenerator = $dataTransportGenerator;
+        $this->modelGenerator = new ModelGenerator($relieseConfiguration);
+        $this->dataTransportObjectGenerator = new DataTransportObjectGenerator($relieseConfiguration);
+        $this->dataTransportObjectGeneratorConfiguration = $relieseConfiguration->getDataTransportGeneratorConfiguration();
     }
 
     /**
@@ -104,8 +128,8 @@ public function fromTableBlueprint(TableBlueprint $tableBlueprint)
         /*
          * Determine the dto Class name and namespace
          */
-        $dtoClassName = $this->dataTransportGenerator->getClassName($tableBlueprint);
-        $dtoFullyQualifiedClassName = $this->dataTransportGenerator->getFullyQualifiedClassName($tableBlueprint);
+        $dtoClassName = $this->dataTransportObjectGenerator->getClassName($tableBlueprint);
+        $dtoFullyQualifiedClassName = $this->dataTransportObjectGenerator->getFullyQualifiedClassName($tableBlueprint);
         $dtoParameterName = ClassNameTool::classNameToParameterName($dtoClassName);
         $dtoParameterType = PhpTypeEnum::objectOfType($dtoFullyQualifiedClassName);
 
@@ -147,6 +171,112 @@ public function fromTableBlueprint(TableBlueprint $tableBlueprint)
 //            dd($dtoPropertyAssignmentStatement);
 //            dd([$modelParameterName, $modelConstant, $dtoParameterName, $setterName]);
         }
+        /*
+         * Look through FK Dto properties and Model FKs to assign DTO
+         */
+        /*
+         * Example:
+        if ($account->relationLoaded('organization')) {
+            $organizationDto = $accountDto->getOrganizationDto() ?? new OrganizationDto();
+            if ($this->organizationMap->toOrganizationDto($account->organization, $organizationDto)) {
+                $accountDto->setOrganizationDto($organizationDto);
+            } else {
+                return false;
+            }
+        }
+         */
+        $requiredMapAccessorTraits = [];
+        $fkDtoProperties = $this->dataTransportObjectGenerator->getForeignKeyDtoPropertyDefinitions($tableBlueprint);
+        foreach ($fkDtoProperties as $fkDtoProperty) {
+//            $fkDtoProperty = $fkDtoProperties[$foreignKeyBlueprint->getName()];
+
+            $fkDtoFullyQualifiedClassName = $fkDtoProperty->getPhpTypeEnum()->getFullyQualifiedObjectClassName();
+            $fkDtoClassName = ClassNameTool::fullyQualifiedClassNameToClassName($fkDtoFullyQualifiedClassName);
+            $fkModelName = \substr($fkDtoClassName, 0, \strlen($fkDtoClassName)-3);
+            $modelRelationshipName = str::snake($fkModelName);
+            $fkModelMapType = $fkModelName.'Map';
+//            $fkModelMapTraitType = $this->modelDataMapGeneratorConfiguration->getAccessorTraitNamespace()
+//                ."\\$fkModelMapType";
+            $fkModelMapGetter = "get$fkModelMapType";
+            $fkModelMapMapping = "to$fkDtoClassName";
+            $relationshipName = str::snake($fkModelName);
+            $fkDtoVariableName = ClassNameTool::dtoClassNameToVariableName($fkDtoClassName);
+            $fkDtoGetterName = ClassNameTool::variableNameToGetterName($fkDtoVariableName);
+            $fkDtoSetterName = ClassNameTool::variableNameToSetterName($fkDtoVariableName);
+
+            if (!\array_key_exists($fkModelMapType, $requiredMapAccessorTraits)) {
+                $requiredMapAccessorTraits[$fkModelMapType]
+                    = $traitDefinition
+                    = $this->generateModelDataMapAccessorTrait($this->getClassNamespaceByType($fkModelMapType));
+                $modelMapAbstractClassDefinition->addTrait(new ClassTraitDefinition($traitDefinition->getFullyQualifiedName()));
+            }
+
+            $statements = new StatementBlockDefinition(
+                /*
+                 * example output:
+                 * if ($account->relationLoaded('organization')) {
+                 */
+                new RawStatementDefinition(\sprintf("if (\$%s->relationLoaded('%s'))", $modelParameterName, $relationshipName))
+            );
+            $statements->addStatementDefinition(
+                new RawStatementDefinition(
+                    /*
+                     * example output:
+                     * $organizationDto = $accountDto->getOrganizationDto() ?? new OrganizationDto();
+                     */
+                    \sprintf(
+                        "\$%s = \$%s->%s() ?? new %s();",
+                        $fkDtoVariableName,
+                        $dtoParameterName,
+                        $fkDtoGetterName,
+                        $fkDtoFullyQualifiedClassName
+                    )
+                )
+            );
+
+            $mapFkDtoStatement = new StatementBlockDefinition(
+                new RawStatementDefinition(
+                    /*
+                     * example output:
+                     * if ($this->getOrganizationMap()->toOrganizationDto($account->organization, $organizationDto))
+                     */
+                    \sprintf(
+                        "if (\$this->%s()->%s(\$%s->%s, \$%s))",
+                        $fkModelMapGetter,
+                        $fkModelMapMapping,
+                        $modelParameterName,
+                        $modelRelationshipName,
+                        $fkDtoVariableName
+                    )
+                )
+            );
+            $mapFkDtoStatement
+                ->addStatementDefinition(
+                    new RawStatementDefinition(
+                        /*
+                         * example output:
+                         * $accountDto->setOrganizationDto($organizationDto);
+                         */
+                        \sprintf(
+                            "\$%s->%s(\$%s);",
+                            $dtoParameterName,
+                            $fkDtoSetterName,
+                            $fkDtoVariableName,
+                        )
+                    )
+                )
+                ->setBlockSuffixStatement(
+                    (new StatementBlockDefinition(new RawStatementDefinition(" else ")))
+                    ->addStatementDefinition(new RawStatementDefinition("return false;"))
+                );
+
+            $statements->addStatementDefinition($mapFkDtoStatement);
+            $mapToDtoMethodDefinition->appendBodyStatement($statements);
+        }
+
+
+
+
         $mapToDtoMethodDefinition->appendBodyStatement(new RawStatementDefinition("return true;"));
 
         $modelMapAbstractClassDefinition->addMethodDefinition($mapToDtoMethodDefinition);
@@ -165,10 +295,26 @@ public function fromTableBlueprint(TableBlueprint $tableBlueprint)
         foreach ($tableBlueprint->getColumnBlueprints() as $columnBlueprint) {
             $modelConstant = '\\'.$modelParameterType->toDeclarationType().'::'.Str::upper($columnBlueprint->getColumnName());
             $getterName = ClassNameTool::columnNameToGetterName($columnBlueprint->getColumnName());
-            $dtoPropertyAssignmentStatement = "\${$modelParameterName}[{$modelConstant}] = \${$dtoParameterName}->{$getterName}();";
-            $mapToDtoMethodDefinition->appendBodyStatement(new RawStatementDefinition($dtoPropertyAssignmentStatement));
-            //            dd($dtoPropertyAssignmentStatement);
-            //            dd([$modelParameterName, $modelConstant, $dtoParameterName, $setterName]);
+            $propertyConstant = ClassPropertyDefinition::getPropertyNameConstantName(
+                ClassNameTool::columnNameToPropertyName($columnBlueprint->getColumnName())
+            );
+
+            $dtoPropertyAssignmentStatement = new RawStatementDefinition(
+                "\${$modelParameterName}[{$modelConstant}] = \${$dtoParameterName}->{$getterName}();"
+            );
+
+            if ($this->dataTransportObjectGeneratorConfiguration->getUseValueStateTracking()) {
+                $conditionalAssignmentBlock
+                    = new StatementBlockDefinition(
+                        new RawStatementDefinition(\sprintf("if (\$%s->getValueWasInitialized(%s::%s))",
+                            $dtoParameterName,
+                            $dtoClassName,
+                            $propertyConstant)));
+                $conditionalAssignmentBlock->addStatementDefinition($dtoPropertyAssignmentStatement);
+                $mapToDtoMethodDefinition->appendBodyStatement($conditionalAssignmentBlock);
+            } else {
+                $mapToDtoMethodDefinition->appendBodyStatement($dtoPropertyAssignmentStatement);
+            }
         }
         $mapToDtoMethodDefinition->appendBodyStatement(new RawStatementDefinition("return true;"));
 
@@ -177,9 +323,68 @@ public function fromTableBlueprint(TableBlueprint $tableBlueprint)
         /*
          * Write the Class Files
          */
+        $codeWriter = new CodeWriter();
+        foreach ($requiredMapAccessorTraits as $traitDefinition) {
+            $traitSource = (new ClassFormatter())->format($traitDefinition);
+            $filePath = $this->modelDataMapGeneratorConfiguration->getAccessorTraitPath().'/'
+            .$traitDefinition->getClassName().".php";
+            $codeWriter->overwriteClassDefinition($filePath, $traitSource);
+        }
+
+        $traitDefinition = $this->generateModelDataMapAccessorTrait($modelMapClassDefinition->getFullyQualifiedName());
+        $traitSource = (new ClassFormatter())->format($traitDefinition);
+        $filePath = $this->modelDataMapGeneratorConfiguration->getAccessorTraitPath().'/'
+            .$traitDefinition->getClassName().".php";
+        $codeWriter->overwriteClassDefinition($filePath, $traitSource);
+
         $this->writeClassFiles($modelMapClassDefinition, $modelMapAbstractClassDefinition);
     }
 
+    public function generateModelDataMapAccessorTrait(string $fullyQualifiedModelDataMapClass) : TraitDefinition
+    {
+        $dataMapClass = ClassNameTool::fullyQualifiedClassNameToClassName($fullyQualifiedModelDataMapClass);
+        $dataMapNamespace = ClassNameTool::fullyQualifiedClassNameToNamespace($fullyQualifiedModelDataMapClass);
+
+        $namespace = $this->modelDataMapGeneratorConfiguration->getAccessorTraitNamespace();
+        $className = ClassNameTool::snakeCaseToClassName('With', $dataMapClass, null);
+
+        $traitDefinition = new TraitDefinition($className, $namespace);
+
+        $traitDefinition
+            ->addClassComment(
+                sprintf("Generated Accessor Trait for %s", $fullyQualifiedModelDataMapClass)
+            )
+            ->addClassComment(
+                "This file is only generated if it does not already exist. To regenerate, remove this file."
+            )
+        ;
+
+        $propertyName = ClassNameTool::classNameToParameterName($dataMapClass);
+        $phpTypeEnum = PhpTypeEnum::nullableObjectOfType($fullyQualifiedModelDataMapClass);
+
+        $getterStatementBlock = (new StatementDefinitionCollection())
+            ->addStatementDefinition(
+                new RawStatementDefinition(
+                    \sprintf(
+                        "return \$this->%s ?? app(%s::class);",
+                        $propertyName,
+                        ClassNameTool::globalClassFQN($fullyQualifiedModelDataMapClass)
+                    )
+                )
+            );
+
+        $property = (new ClassPropertyDefinition($propertyName, $phpTypeEnum))
+            ->withGetter(
+                VisibilityEnum::protectedEnum(),
+                InstanceEnum::instanceEnum(),
+                $getterStatementBlock
+            );
+
+        $traitDefinition->addProperty($property);
+
+        return $traitDefinition;
+    }
+
     /**
      * @param TableBlueprint $tableBlueprint
      *
@@ -235,6 +440,11 @@ public function getAbstractClassNamespace(TableBlueprint $tableBlueprint): strin
         return $this->getClassNamespace($tableBlueprint) .'\\Generated';
     }
 
+    public function getModelMapAccessorTraitMethodName(TableBlueprint $tableBlueprint): string
+    {
+        return 'get'.$this->getClassName($tableBlueprint);
+    }
+
     /**
      * @param ClassDefinition $classDefinition
      * @param ClassDefinition $abstractClassDefinition
@@ -252,10 +462,10 @@ private function writeClassFiles(
         $classFolder = $this->modelDataMapGeneratorConfiguration->getPath();
         $abstractClassFolder = $classFolder . DIRECTORY_SEPARATOR . 'Generated';
         if (!is_dir($classFolder)) {
-            mkdir($classFolder, 0777, true);
+            \mkdir($classFolder, 0755, true);
         }
         if (!is_dir($abstractClassFolder)) {
-            mkdir($abstractClassFolder, 0777, true);
+            \mkdir($abstractClassFolder, 0755, true);
         }
 
         $classFilePath = $classFolder . DIRECTORY_SEPARATOR . $classDefinition->getClassName() . '.php';
@@ -266,4 +476,9 @@ private function writeClassFiles(
         }
         file_put_contents($abstractFilePath, $abstractPhpCode);
     }
+
+    private function getClassNamespaceByType(string $fkModelMapType): string
+    {
+        return $this->modelDataMapGeneratorConfiguration->getNamespace()."\\".$fkModelMapType;
+    }
 }
diff --git a/src/Generator/DataTransport/DataTransportCollectionGenerator.php b/src/Generator/DataTransport/DataTransportCollectionGenerator.php
new file mode 100644
index 00000000..c1aaa533
--- /dev/null
+++ b/src/Generator/DataTransport/DataTransportCollectionGenerator.php
@@ -0,0 +1,13 @@
+dataTransportGeneratorConfiguration = $dataTransportGeneratorConfiguration;
+    public function __construct(
+        RelieseConfiguration $relieseConfiguration
+    ) {
+        $this->dataTransportGeneratorConfiguration = $relieseConfiguration->getDataTransportGeneratorConfiguration();
         /*
          * TODO: inject a MySql / Postgress or other DataType mapping as needed
          */
         $this->dataTypeMap = new MySqlDataTypeMap();
+        $this->dataAttributeGenerator = new DataAttributeGenerator($relieseConfiguration);
     }
 
     /**
@@ -57,17 +88,69 @@ public function __construct(DataTransportGeneratorConfiguration $dataTransportGe
      */
     public function fromTableBlueprint(TableBlueprint $tableBlueprint)
     {
-        $className = $this->getClassName($tableBlueprint);
+        $dtoAbstractClassDefinition = $this->generateAbstractDataTransportObjectClassDefinition($tableBlueprint);
+        $dtoClassDefinition = $this->generateDataTransportObjectClassDefinition($tableBlueprint);
 
-        $abstractClassName = $this->getAbstractClassName($tableBlueprint);
+        /*
+         * Write the Class Files
+         */
+        $this->writeClassFiles($dtoClassDefinition, $dtoAbstractClassDefinition);
+    }
 
-        $namespace = $this->getClassNamespace($tableBlueprint);
+    /**
+     * @param TableBlueprint $tableBlueprint
+     *
+     * @return string
+     */
+    public function getFullyQualifiedClassName(ColumnOwnerInterface $tableBlueprint): string
+    {
+        return $this->getClassNamespace($tableBlueprint).'\\'.$this->getClassName($tableBlueprint);
+    }
 
-        $abstractNamespace = $this->getAbstractClassNamespace($tableBlueprint);
+    public function getClassNamespace(TableBlueprint $tableBlueprint): string
+    {
+        return $this->dataTransportGeneratorConfiguration->getNamespace();
+    }
 
-        $dtoAbstractClassDefinition = new ClassDefinition($abstractClassName, $abstractNamespace);
+    public function getClassName(ColumnOwnerInterface $tableBlueprint): string
+    {
+        return ClassNameTool::snakeCaseToClassName(
+            null,
+            $tableBlueprint->getName(),
+            $this->dataTransportGeneratorConfiguration->getClassSuffix()
+        );
+    }
+
+    public function generateAbstractDataTransportObjectClassDefinition(TableBlueprint $tableBlueprint): ClassDefinition
+    {
+        if (\array_key_exists($tableBlueprint->getUniqueName(),
+            $this->generatedAbstractDataTransportObjectClassDefinitions)
+        ) {
+            return $this->generatedAbstractDataTransportObjectClassDefinitions[$tableBlueprint->getUniqueName()];
+        }
+
+        $dtoAbstractClassDefinition = new ClassDefinition(
+            $this->getAbstractClassName($tableBlueprint),
+            $this->getAbstractClassNamespace($tableBlueprint)
+        );
+
+        if ($this->dataTransportGeneratorConfiguration->getUseValueStateTracking()) {
+            /*
+             * Add interface:
+             *  ValueStateProviderInterface
+             * include:
+             *   use WithValueStateManager;
+             * And bind the tracking in the constructor:
+             *   $this->bindValueChangeStateTracking();
+             */
+            $dtoAbstractClassDefinition
+                ->addInterface(ValueStateProviderInterface::class)
+                ->addTrait(new ClassTraitDefinition(WithValueStateManager::class))
+                ->addConstructorStatement(new RawStatementDefinition("\$this->bindValueChangeStateTracking();"))
+            ;
+        }
 
-        if ($this->dataTransportGeneratorConfiguration->useBeforeChangeObservableProperties()) {
+        if ($this->dataTransportGeneratorConfiguration->getUseBeforeChangeObservableProperties()) {
             $dtoAbstractClassDefinition->addInterface(
                 \PhpLibs\Observable\BeforeValueChangeObservableInterface::class
             );
@@ -76,7 +159,7 @@ public function fromTableBlueprint(TableBlueprint $tableBlueprint)
             );
         }
 
-        if ($this->dataTransportGeneratorConfiguration->useAfterChangeObservableProperties()) {
+        if ($this->dataTransportGeneratorConfiguration->getUseAfterChangeObservableProperties()) {
             $dtoAbstractClassDefinition->addInterface(
                 \PhpLibs\Observable\AfterValueChangeObservableInterface::class
             );
@@ -84,9 +167,6 @@ public function fromTableBlueprint(TableBlueprint $tableBlueprint)
                 new ClassTraitDefinition(\PhpLibs\Observable\AfterValueChangeObservableTrait::class)
             );
         }
-        
-        $dtoClassDefinition = new ClassDefinition($className, $namespace);
-        $dtoClassDefinition->setParentClass($dtoAbstractClassDefinition->getFullyQualifiedName());
 
         foreach ($tableBlueprint->getColumnBlueprints() as $columnBlueprint) {
 
@@ -97,12 +177,21 @@ public function fromTableBlueprint(TableBlueprint $tableBlueprint)
                 $columnBlueprint->getMaximumCharacters(),
                 $columnBlueprint->getNumericPrecision(),
                 $columnBlueprint->getNumericScale(),
-                $columnBlueprint->getIsNullable()
+                // This value must always be true in order to allow for partial DTOs.
+                // Otherwise and error is raised when attempting to read a property that has not been assigned a value
+                true //$columnBlueprint->getIsNullable()
             );
 
+            /*
+             * Use a property defined directly on the class
+             */
             $columnClassProperty = (new ClassPropertyDefinition($propertyName, $phpTypeEnum))
-                ->setIsBeforeChangeObservable($this->dataTransportGeneratorConfiguration->useBeforeChangeObservableProperties())
-                ->setIsAfterChangeObservable($this->dataTransportGeneratorConfiguration->useBeforeChangeObservableProperties())
+                ->setIsBeforeChangeObservable(
+                    $this->dataTransportGeneratorConfiguration->getUseBeforeChangeObservableProperties()
+                )
+                ->setIsAfterChangeObservable(
+                    $this->dataTransportGeneratorConfiguration->getUseAfterChangeObservableProperties()
+                )
                 ->withSetter()
                 ->withGetter()
             ;
@@ -110,34 +199,29 @@ public function fromTableBlueprint(TableBlueprint $tableBlueprint)
             $dtoAbstractClassDefinition->addProperty($columnClassProperty);
         }
 
-        /*
-         * Write the Class Files
-         */
-        $this->writeClassFiles($dtoClassDefinition, $dtoAbstractClassDefinition);
-    }
+        foreach ($this->getForeignKeyDtoPropertyDefinitions($tableBlueprint) as $fkDtoProperty) {
+            $dtoAbstractClassDefinition->addProperty($fkDtoProperty);
+        }
 
-    /**
-     * @param TableBlueprint $tableBlueprint
-     *
-     * @return string
-     */
-    public function getFullyQualifiedClassName(TableBlueprint $tableBlueprint): string
-    {
-        return $this->getClassNamespace($tableBlueprint).'\\'.$this->getClassName($tableBlueprint);
+        return $dtoAbstractClassDefinition;
     }
 
-    public function getClassNamespace(TableBlueprint $tableBlueprint): string
+    public function generateDataTransportObjectClassDefinition(TableBlueprint $tableBlueprint): ClassDefinition
     {
-        return $this->dataTransportGeneratorConfiguration->getNamespace();
-    }
+        if (\array_key_exists($tableBlueprint->getUniqueName(), $this->generatedDataTransportObjectClassDefinitions)) {
+            return $this->generatedDataTransportObjectClassDefinitions[$tableBlueprint->getUniqueName()];
+        }
 
-    public function getClassName(TableBlueprint $tableBlueprint): string
-    {
-        return ClassNameTool::snakeCaseToClassName(
-            null,
-            $tableBlueprint->getName(),
-            $this->dataTransportGeneratorConfiguration->getClassSuffix()
+        $dtoClassDefinition = new ClassDefinition(
+            $this->getClassName($tableBlueprint),
+            $this->getClassNamespace($tableBlueprint)
         );
+        $dtoClassDefinition->setParentClass(
+            $this->generateAbstractDataTransportObjectClassDefinition($tableBlueprint)->getFullyQualifiedName()
+        );
+
+        return $this->generatedDataTransportObjectClassDefinitions[$tableBlueprint->getUniqueName()]
+            = $dtoClassDefinition;
     }
 
     private function getAbstractClassName(TableBlueprint $tableBlueprint): string
@@ -168,10 +252,10 @@ private function writeClassFiles(
         $dtoClassFolder = $this->dataTransportGeneratorConfiguration->getPath();
         $abstractDtoClassFolder = $dtoClassFolder . DIRECTORY_SEPARATOR . 'Generated';
         if (!is_dir($dtoClassFolder)) {
-            mkdir($dtoClassFolder, 0777, true);
+            \mkdir($dtoClassFolder, 0755, true);
         }
         if (!is_dir($abstractDtoClassFolder)) {
-            mkdir($abstractDtoClassFolder, 0777, true);
+            \mkdir($abstractDtoClassFolder, 0755, true);
         }
 
         $dtoFilePath = $dtoClassFolder . DIRECTORY_SEPARATOR . $classDefinition->getClassName() . '.php';
@@ -183,5 +267,178 @@ private function writeClassFiles(
 
         file_put_contents($abstractDtoFilePath, $abstractDtoPhpCode);
     }
+
+    public function generateForeignKeyDtoPropertyDefinition(
+        TableBlueprint $tableBlueprint,
+        ForeignKeyBlueprint $foreignKeyBlueprint
+    ) : ClassPropertyDefinition {
+
+        if (\array_key_exists($foreignKeyBlueprint->getName(), $this->generatedForeignKeyDtoPropertyDefinitions)) {
+            return $this->generatedForeignKeyDtoPropertyDefinitions[$foreignKeyBlueprint->getName()];
+        }
+
+        $fkDtoProperty = null;
+        $dtoVariableName = null;
+
+        $referencedTableBlueprint = $foreignKeyBlueprint->getReferencedTableBlueprint();
+
+        $fkDtoClassName = $this->getClassName($referencedTableBlueprint);
+
+        $dtoVariableName = ClassNameTool::dtoClassNameToVariableName($fkDtoClassName);
+
+        $fkDtoProperty = new ClassPropertyDefinition(
+            $dtoVariableName,
+            PhpTypeEnum::nullableObjectOfType($this->getFullyQualifiedClassName($referencedTableBlueprint))
+        );
+
+        $fkDtoProperty
+            ->setIsBeforeChangeObservable($this->dataTransportGeneratorConfiguration->getUseBeforeChangeObservableProperties())
+            ->setIsAfterChangeObservable($this->dataTransportGeneratorConfiguration->getUseBeforeChangeObservableProperties())
+            ->withSetter()
+            ->withGetter()
+        ;
+
+        $commonColumns = [];
+        /*
+         * Track which columns should be updated when this property is set
+         */
+        foreach ($foreignKeyBlueprint->getFkColumnPairs() as $columns) {
+            /**
+             * @var ColumnBlueprint $referencingColumn
+             * @var ColumnBlueprint $referencedColumn
+             */
+            [$referencingColumn, $referencedColumn] = $columns;
+
+            $commonColumns[$referencingColumn->getColumnName().' = '.$referencedColumn->getColumnName()] =
+                [$referencingColumn, $referencedColumn];
+        }
+
+        foreach ($commonColumns as $columnPairs) {
+            [$referencingColumn, $referencedColumn] = $columnPairs;
+
+            $propertyConstant = ClassPropertyDefinition::getPropertyNameConstantName(
+                ClassNameTool::columnNameToPropertyName($referencedColumn->getColumnName())
+            );
+
+            $propertyGetterCall = \sprintf(
+                "\$%s->%s()",
+                $dtoVariableName,
+                ClassNameTool::columnNameToGetterName($referencedColumn->getColumnName()),
+            );
+
+            /*
+             * If the referenced value was initialized, then set the fk value to match
+             */
+            $ifInitialized = new RawStatementDefinition(
+                sprintf(
+                    "if (\$%s->getValueWasInitialized(%s::%s) && !empty(%s))",
+                    $dtoVariableName,
+                    $fkDtoClassName,
+                    $propertyConstant,
+                    $propertyGetterCall
+                )
+            );
+
+            $setFkFieldIfInitialized = new StatementBlockDefinition($ifInitialized);
+            $setFkFieldIfInitialized->addStatementDefinition(
+                new RawStatementDefinition(
+                    \sprintf(
+                        "\$this->%s(%s);",
+                        ClassNameTool::columnNameToSetterName($referencingColumn->getColumnName()),
+                        $propertyGetterCall,
+                    )
+                )
+            );
+
+            $fkDtoProperty->addAdditionalSetterOperation($setFkFieldIfInitialized);
+        }
+        return $fkDtoProperty;
+    }
+
+    /**
+     * @param TableBlueprint $tableBlueprint
+     * TODO: figure out how to cache these so they are not rebuilt on every call
+     * @return ClassPropertyDefinition[]
+     */
+    public function getForeignKeyDtoPropertyDefinitions(TableBlueprint $tableBlueprint): array
+    {
+        $results = [];
+
+        if (empty($tableBlueprint->getForeignKeyBlueprints())) {
+            return $results;
+        }
+
+        foreach ($tableBlueprint->getForeignKeyBlueprints() as $foreignKeyBlueprint) {
+            $fkDtoProperty = $this->generateForeignKeyDtoPropertyDefinition($tableBlueprint, $foreignKeyBlueprint);
+            $results[] = $fkDtoProperty;
+        }
+        return $results;
+
+        /**
+         * Examine FKs
+         * @var ForeignKeyBlueprint $foreignKeyBlueprint
+         */
+        foreach ($tableBlueprint->getForeignKeyBlueprintsGroupedByReferencedTable() as
+            $referencedTableName => $foreignKeyBlueprints
+        ) {
+            $commonColumns = [];
+            $fkDtoProperty = null;
+            $dtoVariableName = null;
+
+            $referencedTableBlueprint = null;
+            foreach ($foreignKeyBlueprints as $foreignKeyName =>  $foreignKeyBlueprint) {
+
+                $referencedTableBlueprint ??= $foreignKeyBlueprint->getReferencedTableBlueprint();
+
+                $fkDtoClassName = $this->getClassName($referencedTableBlueprint);
+
+                $dtoVariableName = ClassNameTool::dtoClassNameToVariableName($fkDtoClassName);
+                //
+                if (\is_null($fkDtoProperty)) {
+                    $fkDtoProperty = (
+                    new ClassPropertyDefinition(
+                        $dtoVariableName,
+                        PhpTypeEnum::nullableObjectOfType(
+                            $this->getFullyQualifiedClassName($referencedTableBlueprint)
+                        )
+                    )
+                    )
+                        ->setIsBeforeChangeObservable($this->dataTransportGeneratorConfiguration->getUseBeforeChangeObservableProperties())
+                        ->setIsAfterChangeObservable($this->dataTransportGeneratorConfiguration->getUseBeforeChangeObservableProperties())
+                        ->withSetter()
+                        ->withGetter()
+                    ;
+                }
+
+                foreach ($foreignKeyBlueprint->getFkColumnPairs() as $columns) {
+                    /**
+                     * @var ColumnBlueprint $referencingColumn
+                     * @var ColumnBlueprint $referencedColumn
+                     */
+                    [$referencingColumn, $referencedColumn] = $columns;
+
+                    $commonColumns[$referencingColumn->getColumnName().' = '.$referencedColumn->getColumnName()] =
+                        [$referencingColumn, $referencedColumn];
+                }
+            }
+
+            foreach ($commonColumns as $columnPairs) {
+                [$referencingColumn, $referencedColumn] = $columnPairs;
+
+                $fkDtoProperty->addAdditionalSetterOperation(
+                    new RawStatementDefinition(
+                        \sprintf(
+                            "\$this->%s(\$%s->%s());",
+                            ClassNameTool::columnNameToSetterName($referencingColumn->getColumnName()),
+                            $dtoVariableName,
+                            ClassNameTool::columnNameToGetterName($referencedColumn->getColumnName()),
+                        )
+                    )
+                );
+            }
+            $results[] = $fkDtoProperty;
+        }
+        return $results;
+    }
 }
 
diff --git a/src/Generator/Model/Eloquent/EloquentModelGenerator.php b/src/Generator/Model/Eloquent/EloquentModelGenerator.php
deleted file mode 100644
index c93c6651..00000000
--- a/src/Generator/Model/Eloquent/EloquentModelGenerator.php
+++ /dev/null
@@ -1,13 +0,0 @@
-modelGeneratorConfiguration = $modelGeneratorConfiguration;
+        $this->modelGeneratorConfiguration = $relieseConfiguration->getModelGeneratorConfiguration();
         /*
          * TODO: inject a MySql / Postgress or other DataType mapping as needed
          */
@@ -136,6 +138,26 @@ public function getAbstractClassNamespace(TableBlueprint $tableBlueprint): strin
         return $this->getClassNamespace($tableBlueprint) .'\\Generated';
     }
 
+    /**
+     * @param ColumnBlueprint $columnBlueprint
+     *
+     * @return ClassConstantDefinition
+     */
+    public function generateColumnConstantDefinition(ColumnBlueprint $columnBlueprint): ClassConstantDefinition
+    {
+        return new ClassConstantDefinition(
+            ClassNameTool::columnNameToConstantName($columnBlueprint->getColumnName()),
+            $columnBlueprint->getColumnName(),
+            VisibilityEnum::publicEnum()
+        );
+    }
+
+    public function getClassAsVariableName(TableBlueprint $tableBlueprint): string
+    {
+        $name = $this->getClassName($tableBlueprint);
+        return strtolower($name[0]).substr($name, 1);
+    }
+
     /**
      * @return string
      */
diff --git a/src/MetaCode/Definition/ClassDefinition.php b/src/MetaCode/Definition/ClassDefinition.php
index 657496d0..ab227156 100644
--- a/src/MetaCode/Definition/ClassDefinition.php
+++ b/src/MetaCode/Definition/ClassDefinition.php
@@ -2,6 +2,8 @@
 
 namespace Reliese\MetaCode\Definition;
 
+use Reliese\MetaCode\Enum\AbstractEnum;
+use Reliese\MetaCode\Tool\ClassNameTool;
 use RuntimeException;
 
 /**
@@ -9,6 +11,11 @@
  */
 class ClassDefinition implements ImportableInterface, CodeDefinitionInterface
 {
+    /**
+     * @var AbstractEnum
+     */
+    private ?AbstractEnum $abstractEnumType;
+
     /**
      * @var bool[] Array keys are fully qualified interface names
      */
@@ -19,6 +26,26 @@ class ClassDefinition implements ImportableInterface, CodeDefinitionInterface
      */
     private string $className;
 
+    /**
+     * @var string[]
+     */
+    private array $classComments = [];
+
+    /**
+     * @var ClassConstantDefinition[]
+     */
+    private array $constants = [];
+
+    /**
+     * @var ImportableInterface[]
+     */
+    private array $imports = [];
+
+    /**
+     * @var ClassMethodDefinition[]
+     */
+    private array $methods = [];
+
     /**
      * @var string
      */
@@ -40,9 +67,9 @@ class ClassDefinition implements ImportableInterface, CodeDefinitionInterface
     private string $filePath;
 
     /**
-     * @var ImportableInterface[]
+     * @var ClassPropertyDefinition[]
      */
-    private array $imports = [];
+    private array $properties = [];
 
     /**
      * @var ClassTraitDefinition[]
@@ -50,40 +77,55 @@ class ClassDefinition implements ImportableInterface, CodeDefinitionInterface
     private array $traits = [];
 
     /**
-     * @var ClassConstantDefinition[]
-     */
-    private array $constants = [];
-
-    /**
-     * @var ClassPropertyDefinition[]
+     * @param string $comment
+     *
+     * @return $this
      */
-    private array $properties = [];
+    public function addClassComment(string $comment): static
+    {
+        $this->classComments[] = $comment;
+        return $this;
+    }
 
     /**
-     * @var ClassMethodDefinition[]
+     * @param ClassConstantDefinition $constant
+     *
+     * @return $this
      */
-    private array $methods = [];
+    public function addConstant(ClassConstantDefinition $constant): static
+    {
+        $this->constants[$constant->getName()] = $constant;
+        return $this;
+    }
 
     /**
      * ClassDefinition constructor.
      *
-     * @param string $name
-     * @param string $namespace
+     * @param string        $className
+     * @param string        $namespace
+     * @param ?AbstractEnum $abstractEnumType
      */
     public function __construct(
-        string $name,
-        string $namespace
+        string $className,
+        string $namespace,
+        ?AbstractEnum $abstractEnumType = null
     ) {
-        $this->className = $name;
+        $this->className = $className;
         $this->namespace = trim($namespace, '\\');
+        $this->constructorStatementsCollection = new StatementDefinitionCollection();
+        $this->abstractEnumType = $abstractEnumType ?? AbstractEnum::concreteEnum();
     }
 
     /**
      * @param string $fullyQualifiedInterfaceName
+     *
+     * @return $this
      */
-    public function addInterface(string $fullyQualifiedInterfaceName)
+    public function addInterface(string $fullyQualifiedInterfaceName): static
     {
+        $fullyQualifiedInterfaceName = ClassNameTool::globalClassFQN($fullyQualifiedInterfaceName);
         $this->interfaces[$fullyQualifiedInterfaceName] = true;
+        return $this;
     }
 
     /*
@@ -97,12 +139,31 @@ public function setFilePath(string $filePath): ClassDefinition
         return $this;
     }
 
+    /**
+     * @param ImportableInterface $import
+     *
+     * @return $this
+     */
+    public function addImport(ImportableInterface $import): static
+    {
+        // We'll assume this class is already imported to shorten references to itself
+        if (strcmp($import->getFullyQualifiedImportableName(), $this->getFullyQualifiedImportableName()) === 0) {
+            return $this;
+        }
+
+        $this->imports[$import->getImportableName()] = $import;
+        return $this;
+    }
+
     /**
      * @param ClassMethodDefinition $classMethodDefinition
+     *
+     * @return $this
      */
-    public function addMethodDefinition(ClassMethodDefinition $classMethodDefinition)
+    public function addMethodDefinition(ClassMethodDefinition $classMethodDefinition) : static
     {
         $this->methods[$classMethodDefinition->getFunctionName()] = $classMethodDefinition;
+        return $this;
     }
 
     /**
@@ -119,13 +180,27 @@ public function hasInterfaces(): bool
     }
 
     /**
-     * @param string $fullyQualifiedClassName
+     * @param ClassPropertyDefinition $classPropertyDefinition
      *
      * @return $this
      */
-    public function setParentClass(string $fullyQualifiedClassName): ClassDefinition
+    public function addProperty(ClassPropertyDefinition $classPropertyDefinition): ClassDefinition
     {
-        $this->parentClassName = $fullyQualifiedClassName;
+        $this->properties[$classPropertyDefinition->getVariableName()] = $classPropertyDefinition;
+
+        if ($classPropertyDefinition->getIsBeforeChangeObservable()
+            || $classPropertyDefinition->getIsAfterChangeObservable()) {
+
+            $this->addConstant(
+                new ClassConstantDefinition(
+                    ClassPropertyDefinition::getPropertyNameConstantName(
+                        $classPropertyDefinition->getVariableName()
+                    ),
+                    $classPropertyDefinition->getVariableName()
+                )
+            );
+        }
+
         return $this;
     }
 
@@ -134,15 +209,12 @@ public function setParentClass(string $fullyQualifiedClassName): ClassDefinition
      */
     public function hasParentClass(): bool
     {
-        return !is_null($this->parentClassName);
+        return !empty($this->parentClassName);
     }
 
-    /**
-     * @return string
-     */
-    public function getParentClassName() : string
+    public function getClassComments(): array
     {
-        return $this->parentClassName;
+        return $this->classComments;
     }
 
     /**
@@ -153,6 +225,12 @@ public function getClassName(): string
         return $this->className;
     }
 
+    /**
+     * @deprecated use getClassName instead
+     * @return string
+     */
+    public function getName(): string { return $this->getClassName(); }
+
     /**
      * @return string
      */
@@ -170,14 +248,16 @@ public function getNamespace(): string
     }
 
     /**
-     * @param ClassPropertyDefinition $classPropertyDefinition
-     *
-     * @return $this
+     * @return ClassConstantDefinition[]
      */
-    public function addProperty(ClassPropertyDefinition $classPropertyDefinition): ClassDefinition
+    public function getConstants(): array
     {
-        $this->properties[$classPropertyDefinition->getVariableName()] = $classPropertyDefinition;
-        return $this;
+        return $this->constants;
+    }
+
+    public function getFullyQualifiedImportableName(): string
+    {
+        return trim($this->getFullyQualifiedName(), '\\');
     }
 
     /**
@@ -194,14 +274,6 @@ public function addProperties(array $classPropertyDefinitions): ClassDefinition
         return $this;
     }
 
-    /**
-     * @return ClassPropertyDefinition[]
-     */
-    public function getProperties(): array
-    {
-        return $this->properties;
-    }
-
     /**
      * @param string $propertyName
      *
@@ -234,18 +306,17 @@ public function getMethods(): array
         return $this->methods;
     }
 
-    public function addConstant(ClassConstantDefinition $constant): static
+    public function getParentClassName(): string
     {
-        $this->constants[$constant->getName()] = $constant;
-        return $this;
+        return $this->parentClassName;
     }
 
     /**
-     * @return ClassConstantDefinition[]
+     * @return ClassPropertyDefinition[]
      */
-    public function getConstants(): array
+    public function getProperties(): array
     {
-        return $this->constants;
+        return $this->properties;
     }
 
     /**
@@ -273,6 +344,14 @@ public function addTraits(array $traitDefinitions): static
         return $this;
     }
 
+    /**
+     * @return string
+     */
+    public function getStructureType(): string
+    {
+        return 'class';
+    }
+
     /**
      * @return ClassTraitDefinition[]
      */
@@ -297,19 +376,9 @@ public function hasTrait(string $fullyQualifiedTraitName): bool
         return false;
     }
 
-    /**
-     * @param ImportableInterface $import
-     *
-     * @return $this
-     */
-    public function addImport(ImportableInterface $import): static
+    public function setParentClass(string $fullyQualifiedClassName): ClassDefinition
     {
-        // We'll assume this class is already imported to shorten references to itself
-        if (strcmp($import->getFullyQualifiedImportableName(), $this->getFullyQualifiedImportableName()) === 0) {
-            return $this;
-        }
-
-        $this->imports[$import->getImportableName()] = $import;
+        $this->parentClassName = $fullyQualifiedClassName;
         return $this;
     }
 
@@ -351,6 +420,20 @@ public function addConstants(array $constants): static
         return $this;
     }
 
+    private StatementDefinitionCollection $constructorStatementsCollection;
+
+    public function getConstructorStatementsCollection(): StatementDefinitionCollection
+    {
+        return $this->constructorStatementsCollection;
+    }
+
+    public function addConstructorStatement(StatementDefinitionInterface $statementDefinition): static
+    {
+        $this->getConstructorStatementsCollection()
+            ->addStatementDefinition($statementDefinition);
+        return $this;
+    }
+
     /**
      * @todo: Put this on a helper class
      *
@@ -372,14 +455,6 @@ public function getImports(): array
         return $this->imports;
     }
 
-    /**
-     * @return string
-     */
-    public function getFullyQualifiedImportableName(): string
-    {
-        return trim($this->getFullyQualifiedName(), '\\');
-    }
-
     /**
      * @return string
      */
@@ -414,4 +489,12 @@ public function getFilePath(): string
     {
         return $this->filePath;
     }
+
+    /**
+     * @return AbstractEnum
+     */
+    public function getAbstractEnumType(): AbstractEnum
+    {
+        return $this->abstractEnumType;
+    }
 }
diff --git a/src/MetaCode/Definition/ClassPropertyDefinition.php b/src/MetaCode/Definition/ClassPropertyDefinition.php
index 79bc2d39..1d123a01 100644
--- a/src/MetaCode/Definition/ClassPropertyDefinition.php
+++ b/src/MetaCode/Definition/ClassPropertyDefinition.php
@@ -6,6 +6,7 @@
 use Reliese\MetaCode\Enum\InstanceEnum;
 use Reliese\MetaCode\Enum\PhpTypeEnum;
 use Reliese\MetaCode\Enum\VisibilityEnum;
+use Reliese\MetaCode\Tool\ClassNameTool;
 
 /**
  * Class ClassPropertyDefinition
@@ -17,11 +18,21 @@ class ClassPropertyDefinition
      */
     private ?InstanceEnum $getterInstanceEnum = null;
 
+    /**
+     * @var StatementDefinitionInterface|null
+     */
+    private ?StatementDefinitionInterface $getterMethodBody;
+
     /**
      * @var VisibilityEnum|null
      */
     private ?VisibilityEnum $getterVisibilityEnum = null;
 
+    /**
+     * @var StatementDefinitionInterface[]
+     */
+    private array $additionalSetterOperations = [];
+
     /**
      * @var InstanceEnum|null
      */
@@ -134,7 +145,10 @@ public function getSetterInstanceEnum(): InstanceEnum
      */
     public function getSetterMethodDefinition(ClassDefinition $containingClass): ClassMethodDefinition
     {
-        $param = new FunctionParameterDefinition($this->getVariableName(), $this->getPhpTypeEnum());
+        $param = new FunctionParameterDefinition(
+            $this->getVariableName(),
+            $this->getPhpTypeEnum()
+        );
         $setter = new ClassMethodDefinition(
             $this->getSetterMethodName(),
             PhpTypeEnum::staticTypeEnum(),
@@ -145,20 +159,30 @@ public function getSetterMethodDefinition(ClassDefinition $containingClass): Cla
             $this->getSetterInstanceEnum(),
         );
 
-        if ($this->getIsBeforeChangeObservable() && $containingClass->hasTrait('BeforeValueChangeObservableTrait')) {
-            $setter->appendBodyStatement(new RawStatementDefinition(\sprintf("\$this->raiseBeforeValueChange('%s', \$this->%s, \$\%s);\n",
-                        $this->getVariableName(),
+        if ($this->getIsBeforeChangeObservable() ) {
+            $setter->appendBodyStatement(
+                new RawStatementDefinition(
+                    \sprintf(
+                        "\$this->raiseBeforeValueChange(static::%s, \$this->%s, \$%s);\n",
+                        ClassPropertyDefinition::getPropertyNameConstantName($this->getVariableName()),
                         $this->getVariableName(),
-                        $param->getParameterName(),)));
+                        $param->getParameterName(),
+                    )
+                )
+            );
         }
 
         $setter->appendBodyStatement(new RawStatementDefinition(\sprintf("\$this->%s = $%s;\n",
                 $this->getVariableName(),
                 $param->getParameterName())));
 
+        foreach ($this->additionalSetterOperations as $additionalSetterOperation) {
+            $setter->appendBodyStatement($additionalSetterOperation);
+        }
+
         if ($this->getIsAfterChangeObservable() && $containingClass->hasTrait('AfterValueChangeObservableTrait')) {
-            $setter->appendBodyStatement(new RawStatementDefinition(\sprintf("\$this->raiseAfterValueChange('%s', \$this->%s);\n",
-                        $this->getVariableName(),
+            $setter->appendBodyStatement(new RawStatementDefinition(\sprintf("\$this->raiseAfterValueChange(static::%s, \$this->%s);\n",
+                        ClassPropertyDefinition::getPropertyNameConstantName($this->getVariableName()),
                         $this->getVariableName())));
         }
 
@@ -230,16 +254,20 @@ public function setIsBeforeChangeObservable(bool $isBeforeChangeObservable): Cla
     }
 
     /**
-     * @param VisibilityEnum|null $getterVisibilityEnum
-     * @param InstanceEnum|null   $getterInstanceEnum
+     * @param VisibilityEnum|null               $getterVisibilityEnum
+     * @param InstanceEnum|null                 $getterInstanceEnum
+     * @param StatementDefinitionInterface|null $statementDefinitionInterface
      *
      * @return $this
      */
-    public function withGetter(?VisibilityEnum $getterVisibilityEnum = null,
-        ?InstanceEnum $getterInstanceEnum = null): ClassPropertyDefinition
-    {
+    public function withGetter(
+        ?VisibilityEnum $getterVisibilityEnum = null,
+        ?InstanceEnum $getterInstanceEnum = null,
+        ?StatementDefinitionInterface $statementDefinitionInterface = null
+    ): ClassPropertyDefinition {
         $this->getterVisibilityEnum = $getterVisibilityEnum ?? VisibilityEnum::publicEnum();
         $this->getterInstanceEnum = $getterInstanceEnum ?? InstanceEnum::instanceEnum();
+        $this->getterMethodBody = $statementDefinitionInterface;
         return $this;
     }
 
@@ -289,4 +317,48 @@ public function setValue(mixed $value): static
         return $this;
     }
 
+    /**
+     * @param StatementDefinitionInterface $statementDefinition
+     *
+     * @return $this
+     */
+    public function addAdditionalSetterOperation(StatementDefinitionInterface $statementDefinition): static
+    {
+        $this->additionalSetterOperations[] = $statementDefinition;
+        return $this;
+    }
+
+    /**
+     * @param ClassDefinition $classDefinition
+     *
+     * @return ClassMethodDefinition
+     */
+    public function getGetterMethodDefinition(ClassDefinition $classDefinition) : ClassMethodDefinition
+    {
+        return $this->getterMethodDefinition ??= $this->defaultGetterMethodDefinition();
+    }
+
+    /**
+     * @return ClassMethodDefinition
+     */
+    protected function defaultGetterMethodDefinition(): ClassMethodDefinition
+    {
+        $getterFunctionName = ClassNameTool::variableNameToGetterName($this->getVariableName());
+        $getterFunctionType = $this->getPhpTypeEnum();
+
+        $classMethod = new ClassMethodDefinition($getterFunctionName, $getterFunctionType);
+
+        if ($this->getterMethodBody instanceof StatementDefinitionInterface) {
+            $classMethod->appendBodyStatement($this->getterMethodBody);
+        } else {
+            $classMethod->appendBodyStatement(new RawStatementDefinition('return $this->' . $this->getVariableName() . ';'));
+        }
+
+        return $classMethod;
+    }
+
+    public static function getPropertyNameConstantName(string $propertyName): string
+    {
+        return ClassNameTool::identifierNameToConstantName($propertyName)."_PROPERTY";
+    }
 }
diff --git a/src/MetaCode/Definition/ClassTraitDefinition.php b/src/MetaCode/Definition/ClassTraitDefinition.php
index 2db0e598..bb7f0b1e 100644
--- a/src/MetaCode/Definition/ClassTraitDefinition.php
+++ b/src/MetaCode/Definition/ClassTraitDefinition.php
@@ -4,6 +4,7 @@
 namespace Reliese\MetaCode\Definition;
 
 
+use Reliese\MetaCode\Tool\ClassNameTool;
 class ClassTraitDefinition implements ImportableInterface
 {
     private string $name;
@@ -17,11 +18,8 @@ class ClassTraitDefinition implements ImportableInterface
      */
     public function __construct(string $fullyQualifiedTraitName)
     {
-        $parts = explode('\\', \ltrim($fullyQualifiedTraitName, '\\'));
-        $name = \array_pop($parts);
-        $namespace = \implode('\\', $parts);
-        $this->name = $name;
-        $this->namespace = trim($namespace, '\\');
+        $this->name = ClassNameTool::fullyQualifiedClassNameToClassName($fullyQualifiedTraitName);
+        $this->namespace = trim(ClassNameTool::fullyQualifiedClassNameToNamespace($fullyQualifiedTraitName));
     }
 
     /**
diff --git a/src/MetaCode/Definition/CommentBlockStatementDefinition.php b/src/MetaCode/Definition/CommentBlockStatementDefinition.php
new file mode 100644
index 00000000..e2ceee8d
--- /dev/null
+++ b/src/MetaCode/Definition/CommentBlockStatementDefinition.php
@@ -0,0 +1,35 @@
+text[] = $line;
+        return $this;
+    }
+
+    public function toPhpCode(IndentationProviderInterface $indentationProvider, int $blockDepth): string
+    {
+        if (empty($this->text)) {
+            return "";
+        }
+
+        $statements[] = $indentationProvider->getIndentation($blockDepth)."/**";
+        foreach ($this->text as $line) {
+            $statements[] = $indentationProvider->getIndentation($blockDepth).' * '.$line;
+        }
+        $statements[] = $indentationProvider->getIndentation($blockDepth)." */";
+        return \implode("\n", $statements);
+    }
+}
\ No newline at end of file
diff --git a/src/MetaCode/Definition/RawStatementDefinition.php b/src/MetaCode/Definition/RawStatementDefinition.php
index f7cebaf7..736abae6 100644
--- a/src/MetaCode/Definition/RawStatementDefinition.php
+++ b/src/MetaCode/Definition/RawStatementDefinition.php
@@ -2,6 +2,7 @@
 
 namespace Reliese\MetaCode\Definition;
 
+use Reliese\MetaCode\Format\IndentationProviderInterface;
 /**
  * Class RawStatementDefinition
  */
@@ -11,11 +12,11 @@ class RawStatementDefinition implements StatementDefinitionInterface
 
     public function __construct(string $rawPhpCode)
     {
-        $this->rawPhpCode = $rawPhpCode;
+        $this->rawPhpCode = trim($rawPhpCode);
     }
 
-    public function toPhpCode(): string
+    public function toPhpCode(IndentationProviderInterface $indentationProvider, int $blockDepth): string
     {
-        return $this->rawPhpCode;
+        return $indentationProvider->getIndentation($blockDepth).$this->rawPhpCode;
     }
 }
diff --git a/src/MetaCode/Definition/StatementBlockDefinition.php b/src/MetaCode/Definition/StatementBlockDefinition.php
new file mode 100644
index 00000000..883ee6ea
--- /dev/null
+++ b/src/MetaCode/Definition/StatementBlockDefinition.php
@@ -0,0 +1,82 @@
+blockPrefixStatement = $blockPrefixStatement;
+        $this->statementDefinitionCollection = $statementDefinitionCollection ?? new StatementDefinitionCollection();
+    }
+
+    private ?StatementDefinitionInterface $blockSuffixStatement = null;
+    public function addStatementDefinition(StatementDefinitionInterface $statementDefinition) : static
+    {
+        $this->statementDefinitionCollection->addStatementDefinition($statementDefinition);
+        return $this;
+    }
+
+    public function hasStatements(): bool
+    {
+        return $this->statementDefinitionCollection->hasStatements();
+    }
+    /**
+     * @return string
+     */
+    public function toPhpCode(IndentationProviderInterface $indentationProvider, int $blockDepth): string
+    {
+        $prefixStatement = "";
+        $suffixStatement = "";
+
+        if ($this->blockPrefixStatement instanceof StatementDefinitionInterface) {
+            $prefixStatement = $this->blockPrefixStatement->toPhpCode($indentationProvider, $blockDepth)." ";
+        }
+
+        if ($this->blockSuffixStatement instanceof StatementBlockDefinition) {
+            $suffixStatement = " ".ltrim($this->blockSuffixStatement->toPhpCode($indentationProvider, $blockDepth));
+        }
+
+        return \sprintf(
+            "%s{\n%s\n%s}%s\n",
+            $prefixStatement,
+            $this->statementDefinitionCollection->toPhpCode($indentationProvider, $blockDepth + 1),
+            $indentationProvider->getIndentation($blockDepth),
+            $suffixStatement
+        );
+    }
+
+    /**
+     * @param StatementDefinitionInterface|null $blockSuffixStatement
+     *
+     * @return StatementBlockDefinition
+     */
+    public function setBlockSuffixStatement(?StatementDefinitionInterface $blockSuffixStatement): StatementBlockDefinition
+    {
+        $this->blockSuffixStatement = $blockSuffixStatement;
+        return $this;
+    }
+}
diff --git a/src/MetaCode/Definition/StatementDefinitionCollection.php b/src/MetaCode/Definition/StatementDefinitionCollection.php
new file mode 100644
index 00000000..66005d2e
--- /dev/null
+++ b/src/MetaCode/Definition/StatementDefinitionCollection.php
@@ -0,0 +1,43 @@
+statementDefinitions[] = $statementDefinition;
+        return $this;
+    }
+
+    /**
+     * @return string
+     */
+    public function toPhpCode(IndentationProviderInterface $indentationProvider, int $blockDepth): string
+    {
+        $statements = [];
+        foreach ($this->statementDefinitions as $statementDefinition) {
+            $statements[] = $statementDefinition->toPhpCode($indentationProvider, $blockDepth);
+        }
+        return \implode("\n", $statements);
+    }
+
+    public function hasStatements(): bool
+    {
+        return !empty($this->statementDefinitions);
+    }
+}
diff --git a/src/MetaCode/Definition/StatementDefinitionCollectionInterface.php b/src/MetaCode/Definition/StatementDefinitionCollectionInterface.php
new file mode 100644
index 00000000..c5c03222
--- /dev/null
+++ b/src/MetaCode/Definition/StatementDefinitionCollectionInterface.php
@@ -0,0 +1,10 @@
+toDeclarationType()),
+                $exceptionVariableName
+            )
+        );
+
+        $this->setBlockSuffixStatement(
+            (new StatementBlockDefinition(
+                $catchStatement,
+                $catchStatementDefinitionCollection
+            ))
+        );
+
+        return $this;
+    }
+}
diff --git a/src/MetaCode/Enum/AbstractEnum.php b/src/MetaCode/Enum/AbstractEnum.php
index fed94664..9348613c 100644
--- a/src/MetaCode/Enum/AbstractEnum.php
+++ b/src/MetaCode/Enum/AbstractEnum.php
@@ -7,7 +7,7 @@
  */
 class AbstractEnum
 {
-    protected const CONCRETE_TYPE_ID = 0;
+    protected const CONCRETE_TYPE_ID = 10;
 
     protected const ABSTRACT_TYPE_ID = 20;
 
@@ -48,10 +48,10 @@ public static function concreteEnum(): AbstractEnum
         return static::$abstractEnumInstance = new static(static::CONCRETE_TYPE_ID);
     }
 
-    public function toReservedWord() : string
+    public function toReservedWord(bool $includeTrailingSpace = false) : string
     {
         if (static::isAbstract()) {
-            return 'abstract';
+            return 'abstract' . ($includeTrailingSpace ? ' ' : '');
         }
 
         if (static::isConcrete()) {
@@ -68,7 +68,7 @@ public function __toString(): string
         }
         
         if (static::isAbstract()) {
-            return 'static';
+            return 'abstract';
         }
 
         return 'UNKNOWN';
diff --git a/src/MetaCode/Enum/PhpTypeEnum.php b/src/MetaCode/Enum/PhpTypeEnum.php
index 5b9826d3..ab3a0d53 100644
--- a/src/MetaCode/Enum/PhpTypeEnum.php
+++ b/src/MetaCode/Enum/PhpTypeEnum.php
@@ -30,6 +30,13 @@ class PhpTypeEnum
     protected const STATIC_TYPE_ID = 70;
     protected const NULLABLE_STATIC_TYPE_ID = 75;
 
+    protected const NOT_DEFINED_TYPE_ID = 80;
+
+    /**
+     * @var PhpTypeEnum
+     */
+    private static ?PhpTypeEnum $notDefined = null;
+
     private static ?PhpTypeEnum $stringTypeInstance = null;
     private static ?PhpTypeEnum $nullableStringTypeInstance = null;
 
@@ -73,12 +80,25 @@ class PhpTypeEnum
      */
     private bool $isNullable = false;
 
-    private function __construct($phpTypeId, $isNullable = false)
+    private function __construct($phpTypeId, $isNullable)
     {
         $this->phpTypeId = $phpTypeId;
         $this->isNullable = $isNullable;
     }
 
+    public static function notDefined(): PhpTypeEnum
+    {
+        if (static::$notDefined) {
+            return static::$notDefined;
+        }
+        return static::$notDefined = new static(static::NOT_DEFINED_TYPE_ID, true);
+    }
+
+    public function isDefined(): bool
+    {
+        return static::NOT_DEFINED_TYPE_ID !== $this->phpTypeId;
+    }
+
     public function isNullable(): bool
     {
         return $this->isNullable;
@@ -195,7 +215,7 @@ public static function stringType(): PhpTypeEnum
         if (static::$stringTypeInstance) {
             return static::$stringTypeInstance;
         }
-        return static::$stringTypeInstance = new static(static::STRING_TYPE_ID);
+        return static::$stringTypeInstance = new static(static::STRING_TYPE_ID, false);
     }
 
     public static function nullableStringType(): PhpTypeEnum
@@ -211,7 +231,7 @@ public static function intType(): PhpTypeEnum
         if (static::$intTypeInstance) {
             return static::$intTypeInstance;
         }
-        return static::$intTypeInstance = new static(static::INT_TYPE_ID);
+        return static::$intTypeInstance = new static(static::INT_TYPE_ID, false);
     }
 
     public static function nullableIntType(): PhpTypeEnum
@@ -219,7 +239,7 @@ public static function nullableIntType(): PhpTypeEnum
         if (static::$nullableIntTypeInstance) {
             return static::$nullableIntTypeInstance;
         }
-        return static::$nullableIntTypeInstance = new static(static::NULLABLE_INT_TYPE_ID);
+        return static::$nullableIntTypeInstance = new static(static::NULLABLE_INT_TYPE_ID, true);
     }
 
     public static function floatType(): PhpTypeEnum
@@ -227,7 +247,7 @@ public static function floatType(): PhpTypeEnum
         if (static::$floatTypeInstance) {
             return static::$floatTypeInstance;
         }
-        return static::$floatTypeInstance = new static(static::FLOAT_TYPE_ID);
+        return static::$floatTypeInstance = new static(static::FLOAT_TYPE_ID, false);
     }
 
     public static function nullableFloatType(): PhpTypeEnum
@@ -235,7 +255,7 @@ public static function nullableFloatType(): PhpTypeEnum
         if (static::$nullableFloatTypeInstance) {
             return static::$nullableFloatTypeInstance;
         }
-        return static::$nullableFloatTypeInstance = new static(static::NULLABLE_FLOAT_TYPE_ID);
+        return static::$nullableFloatTypeInstance = new static(static::NULLABLE_FLOAT_TYPE_ID, true);
     }
 
     public static function boolType(): PhpTypeEnum
@@ -243,7 +263,7 @@ public static function boolType(): PhpTypeEnum
         if (static::$boolTypeInstance) {
             return static::$boolTypeInstance;
         }
-        return static::$boolTypeInstance = new static(static::BOOL_TYPE_ID);
+        return static::$boolTypeInstance = new static(static::BOOL_TYPE_ID, false);
     }
 
     public static function nullableBoolType(): PhpTypeEnum
@@ -251,7 +271,7 @@ public static function nullableBoolType(): PhpTypeEnum
         if (static::$nullableBoolTypeInstance) {
             return static::$nullableBoolTypeInstance;
         }
-        return static::$nullableBoolTypeInstance = new static(static::NULLABLE_BOOL_TYPE_ID);
+        return static::$nullableBoolTypeInstance = new static(static::NULLABLE_BOOL_TYPE_ID, true);
     }
 
     public static function arrayType(string $containedTypeName): PhpTypeEnum
@@ -259,7 +279,7 @@ public static function arrayType(string $containedTypeName): PhpTypeEnum
         if (\array_key_exists($containedTypeName, static::$arrayTypeInstances)) {
             return static::$arrayTypeInstances[$containedTypeName];
         }
-        return static::$arrayTypeInstances[$containedTypeName] = (new static(static::ARRAY_TYPE_ID))
+        return static::$arrayTypeInstances[$containedTypeName] = (new static(static::ARRAY_TYPE_ID, false))
             ->setContainedTypeName($containedTypeName);
     }
 
@@ -268,7 +288,7 @@ public static function nullableArrayType(string $containedTypeName): PhpTypeEnum
         if (\array_key_exists($containedTypeName, static::$nullableArrayTypeInstances)) {
             return static::$nullableArrayTypeInstances[$containedTypeName];
         }
-        return static::$nullableArrayTypeInstances[$containedTypeName] = (new static(static::NULLABLE_ARRAY_TYPE_ID))
+        return static::$nullableArrayTypeInstances[$containedTypeName] = (new static(static::NULLABLE_ARRAY_TYPE_ID, true))
             ->setContainedTypeName($containedTypeName);
     }
 
@@ -278,7 +298,7 @@ public static function objectOfType(string $fullyQualifiedClassNameOfObjectType)
             return static::$objectTypeInstance[$fullyQualifiedClassNameOfObjectType];
         }
         return static::$objectTypeInstance[$fullyQualifiedClassNameOfObjectType]
-            = (new static(static::OBJECT_TYPE_ID))->setObjectClassType($fullyQualifiedClassNameOfObjectType);
+            = (new static(static::OBJECT_TYPE_ID, false))->setObjectClassType($fullyQualifiedClassNameOfObjectType);
     }
 
     public static function nullableObjectOfType(string $fullyQualifiedClassNameOfObjectType): PhpTypeEnum
@@ -287,7 +307,7 @@ public static function nullableObjectOfType(string $fullyQualifiedClassNameOfObj
             return static::$nullableObjectTypeInstance[$fullyQualifiedClassNameOfObjectType];
         }
         return static::$nullableObjectTypeInstance[$fullyQualifiedClassNameOfObjectType]
-            = (new static(static::NULLABLE_OBJECT_TYPE_ID))->setObjectClassType($fullyQualifiedClassNameOfObjectType);
+            = (new static(static::NULLABLE_OBJECT_TYPE_ID, true))->setObjectClassType($fullyQualifiedClassNameOfObjectType);
     }
 
     public static function staticTypeEnum(): PhpTypeEnum
@@ -295,7 +315,7 @@ public static function staticTypeEnum(): PhpTypeEnum
         if (static::$staticTypeInstance) {
             return static::$staticTypeInstance;
         }
-        return static::$staticTypeInstance = new static(static::STATIC_TYPE_ID);
+        return static::$staticTypeInstance = new static(static::STATIC_TYPE_ID, false);
     }
 
     public static function nullableStaticTypeEnum(): PhpTypeEnum
@@ -303,7 +323,7 @@ public static function nullableStaticTypeEnum(): PhpTypeEnum
         if (static::$nullableStaticTypeInstance) {
             return static::$nullableStaticTypeInstance;
         }
-        return static::$nullableStaticTypeInstance = new static(static::NULLABLE_STATIC_TYPE_ID);
+        return static::$nullableStaticTypeInstance = new static(static::NULLABLE_STATIC_TYPE_ID, true);
     }
 
     public function toDeclarationType() : string
@@ -364,6 +384,10 @@ public function toDeclarationType() : string
             return '?static';
         }
 
+        if (static::NOT_DEFINED_TYPE_ID === $this->phpTypeId) {
+            return '';
+        }
+
         throw new RuntimeException(__METHOD__." Died because ".__CLASS__." was misused.");
     }
 
@@ -425,6 +449,10 @@ public function toAnnotationTypeString() : string
             return 'nullable static';
         }
 
+        if (static::NOT_DEFINED_TYPE_ID === $this->phpTypeId) {
+            return '';
+        }
+
         throw new RuntimeException(__METHOD__." Died because ".__CLASS__." was misused.");
     }
 
@@ -437,6 +465,11 @@ public function __toString(): string
         return __METHOD__.' failed';
     }
 
+    public function getFullyQualifiedObjectClassName(): ?string
+    {
+        return '\\'.trim($this->fullyQualifiedObjectClassName, '\\');
+    }
+
     private function setContainedTypeName(string $containedTypeName) : PhpTypeEnum
     {
         $this->containedTypeName = $containedTypeName;
diff --git a/src/MetaCode/Format/ClassFormatter.php b/src/MetaCode/Format/ClassFormatter.php
index 3cc2b907..c287b247 100644
--- a/src/MetaCode/Format/ClassFormatter.php
+++ b/src/MetaCode/Format/ClassFormatter.php
@@ -16,8 +16,13 @@
 /**
  * Class ClassFormatter
  */
-class ClassFormatter
+class ClassFormatter implements IndentationProviderInterface
 {
+    /**
+     * @param ClassDefinition $classDefinition
+     *
+     * @return string
+     */
     public function format(ClassDefinition $classDefinition): string
     {
         $depth = 0;
@@ -32,6 +37,7 @@ public function format(ClassDefinition $classDefinition): string
         $body[] = $this->formatTraits($classDefinition, $depth);
         $body[] = $this->formatConstants($classDefinition, $depth);
         $body[] = $this->formatProperties($classDefinition, $depth);
+        $body[] = $this->formatConstructor($classDefinition, $depth);
         $body[] = $this->formatMethods($classDefinition, $depth);
 
         $lines[] = "getClassName() . "\n";
+        $lines[] = ' * ' . Str::studly($classDefinition->getStructureType()) . ' ' . $classDefinition->getName() . "\n";
         $lines[] = " * \n";
         $lines[] = " * Created by Reliese\n";
+        foreach ($classDefinition->getClassComments() as $line) {
+            $lines[] = " * \n * ".$line."\n";
+        }
         $lines[] = " */\n";
-        $lines[] = 'class ' . $classDefinition->getClassName();
+        $lines[] = $classDefinition->getAbstractEnumType()->toReservedWord(true)
+            . Str::lower($classDefinition->getStructureType())
+            . ' '
+            . $classDefinition->getClassName();
 
         if (!empty($parent)) {
             $lines[] = ' extends ' . $parent;
@@ -73,7 +85,7 @@ public function format(ClassDefinition $classDefinition): string
      *
      * @return string
      */
-    private function getIndentation(int $depth): string
+    public function getIndentation(int $depth): string
     {
         return str_repeat($this->getIndentationSymbol(), $depth);
     }
@@ -81,7 +93,7 @@ private function getIndentation(int $depth): string
     /**
      * @return string
      */
-    private function getIndentationSymbol(): string
+    public function getIndentationSymbol(): string
     {
         return '    ';
     }
@@ -99,7 +111,9 @@ private function prepareGettersAndSetters(ClassDefinition $classDefinition): voi
                 );
             }
             if ($property->hasGetter()) {
-                $this->appendGetter($property, $classDefinition);
+                $classDefinition->addMethodDefinition(
+                    $property->getGetterMethodDefinition($classDefinition)
+                );
             }
         }
     }
@@ -198,19 +212,21 @@ private function formatProperties(ClassDefinition $classDefinition, int $depth):
 
     private function formatProperty(ClassDefinition $classDefinition, ClassPropertyDefinition $property, int $depth): string
     {
-        $statement = $this->getIndentation($depth)
-            . $property->getVisibilityEnum()->toReservedWord()
-            . ' '
-            . $this->shortenTypeHint($classDefinition, $property->getPhpTypeEnum())
-            . ' $'
-            . $property->getVariableName()
-        ;
-
+        $defaultValueString = '';
         if ($property->hasValue()) {
-            $statement .= ' = ' . var_export($property->getValue(), true);
+            $defaultValueString = ' = '. var_export($property->getValue(), true);
+        } elseif ($property->getPhpTypeEnum()->isNullable()) {
+            $defaultValueString = ' = null';
         }
 
-        return $statement . ';';
+        return $this->getIndentation($depth)
+                . $property->getVisibilityEnum()->toReservedWord()
+                . ' '
+                . $this->shortenTypeHint($classDefinition, $property->getPhpTypeEnum())
+                . ' $'
+                . $property->getVariableName()
+                . $defaultValueString
+                . ';';
     }
 
     /**
@@ -238,19 +254,6 @@ private function appendSetter(ClassPropertyDefinition $property, ClassDefinition
         $classDefinition->addMethodDefinition($getter);
     }
 
-    /**
-     * @param ClassPropertyDefinition $property
-     * @param ClassDefinition $classDefinition
-     */
-    private function appendGetter(ClassPropertyDefinition $property, ClassDefinition $classDefinition): void
-    {
-        $getter = new ClassMethodDefinition('get' . Str::studly($property->getVariableName()),
-            $property->getPhpTypeEnum());
-        $getter->appendBodyStatement(new RawStatementDefinition('return $this->' . $property->getVariableName() . ';'));
-
-        $classDefinition->addMethodDefinition($getter);
-    }
-
     /**
      * @param ClassDefinition $classDefinition
      * @param int $depth
@@ -272,14 +275,14 @@ private function formatMethod(ClassDefinition $classDefinition, ClassMethodDefin
     {
         $signature = $this->getIndentation($depth);
 
-        if ($method->getAbstractEnum()->isAbstract()) {
-            $signature .= $method->getAbstractEnum()->toReservedWord() . ' ';
-        }
-
         if ($method->getVisibilityEnum()) {
             $signature .= $method->getVisibilityEnum()->toReservedWord() . ' ';
         }
 
+        if ($method->getAbstractEnum()->isAbstract()) {
+            $signature .= $method->getAbstractEnum()->toReservedWord() . ' ';
+        }
+
         $signature .= 'function ' . $method->getFunctionName() . '(';
 
         $parameters = [];
@@ -291,16 +294,23 @@ private function formatMethod(ClassDefinition $classDefinition, ClassMethodDefin
 
         $signature .= implode(', ', $parameters);
 
-        $signature .= '): ';
-        $signature .= $this->shortenTypeHint($classDefinition, $method->getReturnPhpTypeEnum());
+        $signature .= ')';
+        if ($method->getReturnPhpTypeEnum()->isDefined()) {
+            /*
+             * This condition is required because constructors do not have return types
+             */
+            $signature .= ": ". $this->shortenTypeHint($classDefinition, $method->getReturnPhpTypeEnum());
+        }
+        if ($method->getAbstractEnum()->isAbstract()) {
+            return $signature . ";\n";
+        }
         $signature .= "\n";
 
         $signature .= $this->getIndentation($depth) . "{\n";
 
         $blockDepth = $depth + 1;
         foreach ($method->getBlockStatements() as $statement) {
-            $signature .= $this->getIndentation($blockDepth)
-                . $statement->toPhpCode()
+            $signature .= $statement->toPhpCode($this, $blockDepth)
                 . "\n";
         }
 
@@ -357,4 +367,16 @@ private function shortenTypeHint(ClassDefinition $classDefinition, PhpTypeEnum $
 
         return $typeHint;
     }
+
+    private function formatConstructor(ClassDefinition $classDefinition, int $depth): string
+    {
+        if (!$classDefinition->getConstructorStatementsCollection()->hasStatements()) {
+            return "";
+        }
+
+        $constructorMethodDefinition = new ClassMethodDefinition('__construct', PhpTypeEnum::notDefined());
+        $constructorMethodDefinition->appendBodyStatement($classDefinition->getConstructorStatementsCollection());
+
+        return $this->formatMethod($classDefinition, $constructorMethodDefinition, $depth+1);
+    }
 }
diff --git a/src/MetaCode/Format/IndentationProviderInterface.php b/src/MetaCode/Format/IndentationProviderInterface.php
new file mode 100644
index 00000000..3e1da368
--- /dev/null
+++ b/src/MetaCode/Format/IndentationProviderInterface.php
@@ -0,0 +1,21 @@
+ ['information_schema', 'performance_schema', 'mysql', 'sys']],
                     // except audit.log_.* tables
-                    ['schemas' => ['audit'], 'tables' => ['/^log_.*$/']],
+                    //['schemas' => ['audit'], 'tables' => ['/^log_.*$/']],
                     // except any table that ends in migrations or matches 'phinx' on all schemas
                     ['schemas' => [$all], 'tables' => ['/^.*migrations$/', 'phinx']],
                     // except soft delete columns on all tables for all schemas
-                    ['schemas' => [$all], 'tables' => [$all], 'columns' => ['deleted_on']]
+                    //['schemas' => [$all], 'tables' => [$all], 'columns' => ['deleted_on']]
                 ],
             ],
             /*
@@ -126,7 +128,7 @@
             |
             */
 
-            'Path' => $appRoot.'Models',
+            'Path' => $appRoot.'/Models',
 
             /*
             |--------------------------------------------------------------------------
@@ -527,18 +529,50 @@
             ],
         ],
         // endregion Model Generator Config
+
+        // region Data Access Generator Config
+        DataAccessGeneratorConfiguration::class => [
+            'Path' => $appRoot.'/DataAccess/PrimaryDatabase',
+            'Namespace' => 'app\DataAccess\PrimaryDatabase',
+            //'ClassPrefix' => '',
+            'ClassSuffix' => 'DataAccess',
+            'ParentClassPrefix' => 'Abstract',
+        ],
+        // endregion Data Access Generator Config
+
+        // region Data Attribute Generator Config
+        DataAttributeGeneratorConfiguration::class => [
+            'Path' => $appRoot.'/DataAttribute/PrimaryDatabase',
+            'Namespace' => 'App\DataAttribute\Objects',
+            'ClassPrefix' => 'With',
+            'ClassSuffix' => 'Trait',
+            'ParentClassPrefix' => 'Abstract',
+        ],
+        // endregion Data Attribute Generator Config
+
         // region Data Transport Generator Config
-        DataTransportGeneratorConfiguration::class => [
+        DataTransportObjectGeneratorConfiguration::class => [
             'Path' => $appRoot.'/DataTransportObjects',
             'Namespace' => 'App\DataTransportObjects',
             'ClassSuffix' => 'Dto',
             'ParentClassPrefix' => 'Abstract',
+            'UseValueStateTracking' => true,
             'ObservableProperties' => [
                 'BeforeChange' => false,
                 'AfterChange' => false,
             ],
         ],
         // endregion Data Transport Generator Config
+
+        // region Data Transport Collection Generator Config
+        DataTransportCollectionGeneratorConfiguration::class => [
+            'Path' => $appRoot.'/DataTransport/Collections',
+            'Namespace' => 'App\DataTransport\Collections',
+            'ClassSuffix' => 'Dto',
+            'ParentClassPrefix' => 'Abstract',
+        ],
+        // endregion Data Transport Collection Generator Config
+
         // region Data Map Generator Config
         ModelDataMapGeneratorConfiguration::class => [
             'Path' => $appRoot.'/DataMaps/PrimaryDatabase',
diff --git a/tests/Behat/Contexts/Generator/DataTransportObjectGeneratorContext.php b/tests/Behat/Contexts/Generator/DataTransportObjectGeneratorContext.php
index b0ee04be..ac895b1a 100644
--- a/tests/Behat/Contexts/Generator/DataTransportObjectGeneratorContext.php
+++ b/tests/Behat/Contexts/Generator/DataTransportObjectGeneratorContext.php
@@ -2,24 +2,12 @@
 
 namespace Tests\Behat\Contexts\Generator;
 
-use Behat\Behat\Tester\Exception\PendingException;
-use Reliese\Generator\DataTransport\DataTransportGenerator;
 use Reliese\Generator\DataTransport\DataTransportObjectGenerator;
-use Tests\Behat\Contexts\FeatureContext;
 /**
  * Class DataTransportObjectGeneratorContext
  */
 class DataTransportObjectGeneratorContext extends GeneratorContexts
 {
-    public function getDataTransportObjectGenerator(): DataTransportObjectGenerator
-    {
-        return new DataTransportObjectGenerator(
-            $this->getConfigurationContexts()
-                 ->getDataTransportObjectGeneratorConfigurationContext()
-                 ->getDataTransportObjectGeneratorConfiguration()
-        );
-    }
-
     /**
      * @When /^DataTransportObjectGenerator generates Abstract Dto from Schema "([^"]*)" Table "([^"]*)"$/
      */
diff --git a/tests/MetaCode/Format/ClassFormatterTest.php b/tests/MetaCode/Format/ClassFormatterTest.php
index 9189a467..eacb37fb 100644
--- a/tests/MetaCode/Format/ClassFormatterTest.php
+++ b/tests/MetaCode/Format/ClassFormatterTest.php
@@ -356,7 +356,7 @@ public function it_formats_a_class_with_one_property_of_type_nullable_global_obj
  */
 class OneClass
 {
-    private ?DateTime \$aProperty;
+    private ?DateTime \$aProperty = null;
 }
 
 PHP;
@@ -393,8 +393,8 @@ public function it_formats_a_class_with_two_properties_of_type_nullable_global_o
  */
 class OneClass
 {
-    private ?DateTime \$aProperty;
-    private ?DateTime \$anotherProperty;
+    private ?DateTime \$aProperty = null;
+    private ?DateTime \$anotherProperty = null;
 }
 
 PHP;
@@ -431,7 +431,7 @@ public function it_formats_a_class_with_one_nullable_property()
  */
 class OneClass
 {
-    private ?string \$aProperty;
+    private ?string \$aProperty = null;
 }
 
 PHP;
@@ -807,7 +807,6 @@ class OneClass
     public function setOneProperty(string \$oneProperty): static
     {
         \$this->oneProperty = \$oneProperty;
-
         return \$this;
     }
 
@@ -831,6 +830,6 @@ public function getOneProperty(): string
 
         $classOutput = $classFormatter->format($classDefinition);
 
-        $this->assertEquals($expectedClassOutput, $classOutput);
+        $this->assertEquals($expectedClassOutput, $classOutput, "\n $expectedClassOutput \n VERSUS \n $classOutput");
     }
 }