{"id":27076362,"url":"https://github.com/mjul/diesel","last_synced_at":"2025-04-06T00:31:36.829Z","repository":{"id":8613750,"uuid":"10254709","full_name":"mjul/diesel","owner":"mjul","description":"Diesel is a DSL toolkit for .NET code generation for DDD","archived":false,"fork":false,"pushed_at":"2013-11-06T14:29:48.000Z","size":944,"stargazers_count":16,"open_issues_count":0,"forks_count":1,"subscribers_count":5,"default_branch":"master","last_synced_at":"2025-03-12T06:38:13.059Z","etag":null,"topics":["c-sharp","code-generator","ddd","dsl","event-sourcing","parser-combinators"],"latest_commit_sha":null,"homepage":null,"language":"C#","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/mjul.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGELOG.md","contributing":null,"funding":null,"license":"LICENSE.txt","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null}},"created_at":"2013-05-23T22:19:21.000Z","updated_at":"2025-01-20T14:43:42.000Z","dependencies_parsed_at":"2022-08-24T02:51:06.075Z","dependency_job_id":null,"html_url":"https://github.com/mjul/diesel","commit_stats":null,"previous_names":[],"tags_count":0,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/mjul%2Fdiesel","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/mjul%2Fdiesel/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/mjul%2Fdiesel/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/mjul%2Fdiesel/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/mjul","download_url":"https://codeload.github.com/mjul/diesel/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":247419597,"owners_count":20936009,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["c-sharp","code-generator","ddd","dsl","event-sourcing","parser-combinators"],"created_at":"2025-04-06T00:30:43.435Z","updated_at":"2025-04-06T00:31:36.815Z","avatar_url":"https://github.com/mjul.png","language":"C#","funding_links":[],"categories":[],"sub_categories":[],"readme":"# Diesel is a DSL toolkit for .NET code generation for DDD\n\nDiesel provides a declarative language for generating code for your .NET projects.\n\n* __Value Types__ - strong types for value types, e.g. struct EmployeeNumber instead of int. \n* __Commands__ - generates classes for the Command DTOs.\n* __Domain Events__ - generates classes for the Domain Event DTOs.\n* __DTOs__ - generate other Data Transfer Objects: classes and enums tagged for serialization.\n* __Application Services__ - generate an interface for all the commands accepted by the service.\n\nPlanned features include declarations for Value Objects, Aggregate Roots and Projections.\n\n\n## Example\n\nCreate a model in the Diesel DSL language:\n\n```\n    (namespace Employees\n        (defvaluetype EmployeeNumber)\n        (defvaluetype EmployeeName (string FirstName, string LastName))\n        (defdto Name (string FirstName, string LastName))\n        (defapplicationService ImportService\n            (defcommand ImportEmployee (int EmployeeNumber, Name Name))\n        (defdomainevent EmployeeImported (Guid Id, int EmployeeNumber, Name Name)))\n```\n\nUse the Visual Studio T4 template to automatically generate the code (see below) or compile it the model source \nwith a single method call: `DieselCompiler.Compile(modelSource)`. \n\nNow use it\n\n```csharp\n\n    // value types automatically get a constructor setting their value\n    var founderNumber = new EmployeeNumber(1);\n    var lateHireNumber = new EmployeeNumber(100);\n            \n    // value types can have multiple fields \n    var name = new EmployeeName(\"Martin\", \"Jul\");\n\n    // You get free value equality and equality operators \n    var isFounder = (employee.EmployeeNumber == founderNumber);\n         \n    // Commands are classes and have a constructor that assigns all properties\n    var command = new ImportEmployeeCommand(1, new Name(\"Martin\", \"Jul\"));\n           \n    // The corresponding properties are automatically added to the classes:\n    var firstName = command.Name.FirstName;\n     \n```\n\n# Benefits\n\nDiesel saves you from writing a lot of trivial code that no humans should write by hand,\nso you can focus on building the essential parts of your application:\n\n* Get your Value Objects at huge discounts - no more excuses for [Primitive Obsession](http://c2.com/cgi/wiki?PrimitiveObsession)\n* DTOs are on sale, too: all you need to do is writing the constructor signature.\n* Get consistent serializability of Commands, Domain Events and other DTOs.\n\n  \n# Defining Simple Value Types\n\n    (defvaluetype \u003ctypename\u003e)\n    (defvaluetype \u003ctypename\u003e \u003ctype\u003e)\n    (defvaluetype \u003ctypename\u003e \u003cproperties\u003e)\n\nWhen you compile the `defvaluetype`, Diesel generates .NET code in your language, \nemitting a  value type (`struct` in C#) with the declared properties, \na constructor to set them and equality operator overloads to give it full value equality semantics.\n\nIn the first two cases, where properties are not declared the property name is always `Value`.\nThe type is optional, it defaults to Int32.\n\nIf multiple properties are desired, the list of properties and their types must be declared.\n\n\n## Examples\n\n    (defvaluetype EmployeeNumber)\n    (defvaluetype Amount Decimal)\n    (defvaluetype EmployeeName (string First, string Last))\n\nThe single-property declaration can also be written in the property list format, i.e. the `Amount` \ntype above can also be declared as `(defvaluetype Amount (Decimal Value))`.\n\nThe code generation is straight-forward, for example, the declaration \n`(defvaluetype EmployeeNumber)` yields a type with value semantics and\nequality operator overloads like this:\n\n```csharp\n\n    [System.SerializableAttribute()]\n    [System.Diagnostics.DebuggerDisplayAttribute(\"{Value}\")]\n    public partial struct EmployeeNumber : System.IEquatable\u003cEmployeeNumber\u003e {\n        \n        private int _value;\n        \n        public EmployeeNumber(int value) {\n            this._value = value;\n        }\n        \n        public int Value {\n            get {\n                return this._value;\n            }\n        }\n        \n        public static bool operator ==(EmployeeNumber left, EmployeeNumber right) {\n            return left.Equals(right);\n        }\n        \n        public static bool operator !=(EmployeeNumber left, EmployeeNumber right) {\n            return (false == left.Equals(right));\n        }\n        \n        public override int GetHashCode() {\n            return (0 + this.Value.GetHashCode());\n        }\n        \n        public bool Equals(EmployeeNumber other) {\n            return (true \n                        \u0026\u0026 (this.Value == other.Value));\n        }\n        \n        public override bool Equals(object obj) {\n            if (object.ReferenceEquals(null, obj)) {\n                return false;\n            }\n            return (typeof(EmployeeNumber).IsAssignableFrom(obj.GetType()) \u0026\u0026 this.Equals(((EmployeeNumber)(obj))));\n        }\n    }\n\n```\n\n# Defining Commands\n\n    (defcommand \u003ctypename\u003e \u003cproperties\u003e)\n\nThis defines a class representing the Command, the properties are assigned via a constructor\nand equals and equality operators are implemented with value semantics.\n\nCommands are DTOs (Data Transfer Objects), so they are also decorated with attributes\nto allow them to be serializable with the BinarySerializer and the DataContractSerializer.\n\nCommand can be generated with base types, see `defconventions` for a description of \nhow to configure this. \n\nDo note, however, that since Commands are contracts it is generally best to declare base\n __interfaces__ only so that the risk of breaking contracts by modifying a base is \nminimal.\n\n\n## Example\n\n    (defcommand ImportEmployee (int EmployeeNumber, string FirstName, string LastName, int? SourceId))\n\nThis generates a class with properties `EmployeeNumber`, `FirstName` and `LastName` and `SourceId`.\nNullable types are supported in properties with C# short syntax, i.e. \"int?\" denotes a nullable Int32.\n\n\n## Adding Base Classes or Interfaces to Domain Events \n\nThe code generator reads conventions from the optional `defconventions` declaration\nat the top of the Diesel source file.\n\nYou can use this to add base types to the generated Commands. \nFor example, to have all Commands derive from the `GreatApp.ICommand` interface, \njust add this declaration:\n\n    (defconventions :commands {:inherit [GreatApp.IDomainEvent]})\n\nNote that since Commands are contracts it is generally best to \ndeclare base __interfaces__ only so that the risk of breaking contracts by modifying a base is \nminimal.\n\nSee the section on defining conventions for a full description of `defconventions`.\n\n\n\n\n\n# Defining Domain Events\n\n    (defdomainevent \u003ctypename\u003e \u003cproperties\u003e)\n\nThis defines a class representing the Domain Event, a constructor to assign its properties\nand equals and equality operators are implemented with value semantics.\n\nLike Commands, Domain Events are DTOs, so they are also decorated with attributes\nto allow them to be serializable with the BinarySerializer and the DataContractSerializer.\n\n## Example\n\n    (defdomainevent EmployeeImported (Guid Id, int EmployeeNumber, \n                                      string FirstName, string LastName, int? SourceId))\n\nThis generates a class with properties `Id`, `EmployeeNumber`, `FirstName` and `LastName` and `SourceId`.\n\n\n## Adding Base Classes or Interfaces to Domain Events \n\nThe code generator reads conventions from the optional `defconventions` declaration\nat the top of the Diesel source file.\n\nYou can use this to add base types to the generated Domain Events. \nFor example, to have all Domain Events derive from the `GreatApp.IDomainEvent` interface, \njust add this declaration:\n\n    (defconventions :domainevents {:inherit [GreatApp.IDomainEvent]})\n\nNote that since Domain Events are contracts it is generally best to \ndeclare base __interfaces__ only so that the risk of breaking contracts by modifying a base is \nminimal.\n\nSee the section on defining conventions for a full description of `defconventions`.\n\n\n# Defining Data Transfer Objects (DTOs)\n\n    (defdto \u003ctypename\u003e \u003cproperties\u003e)\n\nThis defines a class representing the DTO with the specified properties.\nIt adds a constructor to set all properties.\nEquals and equality operators are implemented with value semantics.\n\nBeing a DTO (Data Transfer Objects), the type is decorated with attributes\nto make it serializable with the BinarySerializer and the DataContractSerializer.\n\nThe properties are specified in the C# constructor parameter syntax. \n\n## Example\n\n    (defdto Name (string FirstName, string LastName))\n\nThis generates a class `Name` with properties `FirstName` and `LastName`.\n\n\n# Defining Enum DTOs\n\n    (defenum \u003ctypename\u003e \u003cvalues\u003e)\n\nThis defines an enum with the specified values.\n\nBeing a DTO (Data Transfer Objects), the type is decorated with attributes\nto make it serializable with the BinarySerializer and the DataContractSerializer.\n\n## Example\n\n    (defenum Gender [Female Male])\n\nThis generates an enum `Gender` with values `Female` and `Male`\nand the corresponding DataContract attributes.\n\n\n# Defining Application Services\n\n    (defapplicationservice \u003cname\u003e \u003ccommands+\u003e)\n\nThis defines an Application Service of the given name. \n\nThe code generator emits an interface with Execute overloads for each of the commands\ndefined in the scope of the application service.\n\n### Example\n\nGiven the following declaration\n\n    (defapplicationservice ImportService\n        (defcommand ImportEmployee (Guid CommandId, int EmployeeNumber, string FirstName, string LastName, int? SourceId))\n        (defcommand ImportConsultant (string FirstName, string LastName, string Company)))\n\nThis declares the `ImportEmployee` class as defined by the nested `defcommand` and the following\ninterface for the service:\n\n```csharp\n    public partial interface IImportService {\n         void Execute(ImportEmployee command);\n         void Execute(ImportConsultant command);\n    }\n```\n\n# Defining Namespaces\n\n    (namespace \u003cname\u003e \u003ctypedeclarations*\u003e)\n\nA model consists of one or more namespace declarations. Namespaces work like in .NET, controlling \nthe .NET namespace of the types declared inside.\n\n## Example\n\n```\n    (namespace Employees\n        (defvaluetype EmployeeNumber)\n        (defapplicationService ImportService\n            (defcommand ImportEmployee (int EmployeeNumber, string FirstName, string LastName))\n        (defdomainevent EmployeeImported (Guid Id, int EmployeeNumber, string FirstName, string LastName)))\n```\n\nThis declares types `Employees.EmployeeNumber`, `Employees.IImportService`, `Employees.ImportEmployee`, \nand `Employees.EmployeeImported`.\n\n\n# Defining Conventions for Code-Generation\n\n    (defconventions :domainevents {:inherit \u003clist-of-base-types\u003e}\n                    :commands {:inherit \u003clist-of-base-types\u003e})\n\nThe code-generation conventions can be controlled through the `defconventions` declaration.\nFor now, it only controls the list of interfaces and base classes for the Domain Events and Commands.\n\nThe declaration must be placed first in the source file. The `:domainevents` and `:commands`\ndeclarations inside are both optional and can be in any order, but they can not occur more than\nonce.\n\n## Example\n\n    (defconventions :domainevents {:inherit [Test.Diesel.IDomainEvent]}\n                    :commands {:inherit [Test.Diesel.ICommand]})\n\nThis causes the code generator to add the `Test.Diesel.IDomainEvent` interface \nas a base on all Domain Events, and the `Test.Diesel.ICommand` interface to all Commands.\n\n\n## Example: Adding interfaces to Domain Events\n\n    (defconventions :domainevents {:inherit [Test.Diesel.IDomainEvent]})\n\nThis adds the `Test.Diesel.IDomainEvent` as a base on all generated Domain Events.\n\n## Example: Adding interfaces to Commands\n\n    (defconventions :commands {:inherit [Test.Diesel.ICommand]})\n\nThis adds the `Test.Diesel.ICommand` as a base on all generated Domain Events.\n\n\n# Comments\n\nSingle-line comments are supported. They begin with one (or more) semicolons and \ncontinue to the end of the line.\n\nComments are allowed between the high-level syntactic elements, such as namespaces,\nservices, convetions and type declarations such as DTOs, enums, Domain Events, Services and Commands.\n\nComments are generally not allowed _inside_ the elements, _e.g._ between parameters in a parameter declaration.\nThe reason for this is that the inside is typically a C# syntax property list, which has a different \ncomment syntax than Diesel.\n\n## Example\n\n```\n    ;; Declare Employees namespace\n    (namespace Employees\n        ;; This a comment\n        (defvaluetype EmployeeNumber)\n        (defapplicationService ImportService\n            ;; Comments can also be nested\n            (defcommand ImportEmployee (int EmployeeNumber, string FirstName, string LastName))\n        (defdomainevent EmployeeImported (Guid Id, int EmployeeNumber, string FirstName, string LastName)))\n```\n\n\n# Using the T4 Template\n\nThe Test project contains a Visual Studio T4 template (.tt file) under Generated.\nIt can be used as a template for adding code generation to your own project.\nIt depends on the Diesel compiler and its dependencies being available in binary form \nin the project (assemblies).\n\nTo use it, you just need to add two files to your project: the model generator, and the \nmodel source code in the Diesel language.\n\nHere is the `GenerateModel.tt`  T4 template from one project, you need to adapt the name \nof your model file `MetaModel.diesel` and the absolute paths to `Sprache` and `Diesel`.\nAfter this, you just \"Run Custom Tool\" on the T4 file to regenerate your model from the \nDiesel specification in `MetaModel.diesel`.\n\n    \u003c#@ template debug=\"false\" hostspecific=\"true\" language=\"C#\" #\u003e\n    \u003c#@ assembly name=\"System.Core\" #\u003e\n    \u003c#@ import namespace=\"System.IO\" #\u003e\n    \u003c#@ import namespace=\"System.Linq\" #\u003e\n    \u003c#@ import namespace=\"System.Text\" #\u003e\n    \u003c#@ import namespace=\"System.Collections.Generic\" #\u003e\n    \u003c#@ assembly name=\"EnvDTE\" #\u003e\n    \u003c#@ import namespace=\"EnvDTE\" #\u003e\n    \u003c#@ assembly name=\"$(SolutionDir)Packages\\Sprache.1.10.0.28\\lib\\net40\\Sprache.dll\" #\u003e\n    \u003c#@ assembly name=\"$(SolutionDir)Packages\\Diesel.1.2.0.0\\lib\\net45\\Diesel.dll\" #\u003e\n    \u003c#@ import namespace=\"Diesel\" #\u003e\n    \u003c#@ output extension=\".cs\" #\u003e\n    \u003c# \n        var model = this.Host.ResolvePath(\"MetaModel.diesel\");\n        var source = File.ReadAllText(model);\n        var output = DieselCompiler.Compile(source);\n    #\u003e\n    \u003c#= output #\u003e\n\n# NuGet Package\n\nThe library is available on [NuGet](https://nuget.org/packages/Diesel/) under the package name \"Diesel\".\n\nTo install: \n\n        PM\u003e Install-Package Diesel    \n\n# License\nCopyright (C)2013 Martin Jul (www.mjul.com)\n\nDistributed under the MIT License. See the LICENSE.txt file for details.\n\n## Dependencies\n\n### Sprache Parser Combinator\nThis library uses the amazing [Sprache](https://github.com/sprache/Sprache) parser combinator to easily \nparse the model language. It rocks! \n\nSprache has the following license:\n\n    The MIT License\n\n    Copyright (c) 2011 Nicholas Blumhardt\n\n    Permission is hereby granted, free of charge, to any person obtaining a copy\n    of this software and associated documentation files (the \"Software\"), to deal\n    in the Software without restriction, including without limitation the rights\n    to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n    copies of the Software, and to permit persons to whom the Software is\n    furnished to do so, subject to the following conditions:\n\n    The above copyright notice and this permission notice shall be included in\n    all copies or substantial portions of the Software.\n\n    THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n    IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n    FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n    AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n    LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n    OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\n    THE SOFTWARE.\n\n\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fmjul%2Fdiesel","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fmjul%2Fdiesel","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fmjul%2Fdiesel/lists"}