-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathdomain.html
2 lines (2 loc) · 125 KB
/
domain.html
1
2
<!DOCTYPE html><html lang="en"><head><meta charset="utf-8"><meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no"><meta name="apple-mobile-web-app-capable" content="yes"><meta name="keywords" content="i18n, internationalization, translation, javascript, nodejs, js"><title>cqrs - domain</title><link href="../public/css/index.css" rel="stylesheet"><script src="../public/js/jquery-1.7.2.min.js"></script><script src="../public/js/bootstrap-2.0.2.min.js"></script><!-- HTML5 shim, for IE6-8 support of HTML5 elements--><!--[if lt IE 9]><script src="http://html5shim.googlecode.com/svn/trunk/html5.js"></script><![endif]--><!--[if lt IE 8]><link href="../public/css/font-awesome-ie7-2.0.css" rel="stylesheet" type="text/css"><![endif]--></head><body><header class="header"><div class="header-inner"><div class="navbar navbar-fixed-top"><div class="navbar-inner"><div class="container"><!--.btn-navbar is used as the toggle for collapsed navbar content--><a data-toggle="collapse" data-target=".nav-collapse" class="btn btn-navbar"><span class="icon-bar"></span><span class="icon-bar"></span><span class="icon-bar"></span></a><!-- Be sure to leave the brand out there if you want it shown--><a href="../" class="brand">cqrs</a><!-- Everything you want hidden at 940px or less, place within here--><div class="nav-collapse"><ul class="nav"><li><a href="../index.html">Home</a></li><li><a href="../pages/domain.html">domain</a></li><li><a href="../pages/viewmodel.html">viewmodel</a></li><li><a href="../pages/eventdenormalizer.html">eventdenormalizer</a></li><li><a href="http://adrai.github.io/node-eventstore/">eventstore</a></li><li><a href="http://jamuhl.github.com/backbone.CQRS/">backbone.CQRS</a></li><li><a href="https://github.com/KABA-CCEAC/angular.CQRS/">angular.CQRS</a></li><li><a href="../pages/eventedcommand.html">evented-command</a></li><li><a href="../pages/saga.html">saga</a></li></ul></div></div></div></div></div></header><div class="main"><div class="main-inner"><div class="container"><div class="content-container"> <div class="documentation"><div class="row-fluid"><div class="span8"><div class="hero-unit"><h2>cqrs-domain</h2><p>cqrs-domain is a node.js module based on node-eventstore. It can be very useful as domain component if you work with (d)ddd, cqrs, eventdenormalizer, host, etc.</p></div></div><div class="span4 downloads"><p>node.js:</p><pre><code><span class="pln">npm install cqrs</span><span class="pun">-</span><span class="pln">domain</span></code></pre><p>Build status:<a href="http://travis-ci.org/adrai/node-cqrs-domain"><img src="https://secure.travis-ci.org/adrai/node-cqrs-domain.png"></a></p><p>Release:<a href="https://npmjs.org/package/cqrs-domain"><img src="https://img.shields.io/npm/v/cqrs-domain.svg"></a></p><div style="margin-top: 25px;" class="alert alert-info feature-description"><a href="https://github.com/adrai/node-cqrs-domain"><i class="icon-github"></i> fork me on github</a><br><a href="https://github.com/adrai/node-cqrs-domain/issues"><i class="icon-github"></i> issues</a><br><a href="https://github.com/adrai/node-cqrs-domain/blob/master/releasenotes.md"><i class="icon-github"></i> release notes</a></div></div></div><div class="row-fluid"><div class="span12"><h2>Workflow</h2><div class="row-fluid"><div class="span12"><div class="feature"><pre><code><span class="pln"> </span><span class="pun">│</span><span class="pln"><br /> cmd<br /> </span><span class="pun">│</span><span class="pln"><br /> </span><span class="pun">∨</span><span class="pln"><br /> </span><span class="pun">╔════════════╗</span><span class="pln"><br /> </span><span class="pun">║</span><span class="pln"> validation </span><span class="pun">║─────────></span><span class="pln"> </span><span class="str">"rejected"</span><span class="pln"><br /> </span><span class="pun">╚════════════╝</span><span class="pln"><br /> </span><span class="pun">│</span><span class="pln"><br /> cmd<br /> </span><span class="pun">│</span><span class="pln"><br /> </span><span class="pun">∨</span><span class="pln"><br /></span><span class="pun">╔═════════════════════╗</span><span class="pln"><br /></span><span class="pun">║</span><span class="pln"> pre</span><span class="pun">-</span><span class="pln">load</span><span class="pun">-</span><span class="pln">conditions </span><span class="pun">║─────></span><span class="pln"> </span><span class="str">"rejected"</span><span class="pln"><br /></span><span class="pun">╚═════════════════════╝</span><span class="pln"><br /> </span><span class="pun">│</span><span class="pln"><br /> cmd<br /> </span><span class="pun">│</span><span class="pln"><br /> </span><span class="pun">∨</span><span class="pln"><br /></span><span class="pun">╔════════════════╗</span><span class="pln"><br /></span><span class="pun">║</span><span class="pln"> pre</span><span class="pun">-</span><span class="pln">conditions </span><span class="pun">║─────></span><span class="pln"> </span><span class="str">"rejected"</span><span class="pln"><br /></span><span class="pun">╚════════════════╝</span><span class="pln"><br /> </span><span class="pun">│</span><span class="pln"><br /> cmd<br /> </span><span class="pun">│</span><span class="pln"><br /> </span><span class="pun">∨</span><span class="pln"><br /> </span><span class="pun">╔════════════╗</span><span class="pln"><br /> </span><span class="pun">║</span><span class="pln"> handle cmd </span><span class="pun">║</span><span class="pln"><br /> </span><span class="pun">╚════════════╝</span><span class="pln"><br /> </span><span class="pun">│</span><span class="pln"><br /> evt<br /> </span><span class="pun">│</span><span class="pln"><br /> </span><span class="pun">∨</span><span class="pln"><br /> </span><span class="pun">╔═══════════╗</span><span class="pln"><br /> </span><span class="pun">║</span><span class="pln"> apply evt </span><span class="pun">║</span><span class="pln"><br /> </span><span class="pun">╚═══════════╝</span><span class="pln"><br /> </span><span class="pun">│</span><span class="pln"><br /> </span><span class="pun">│</span><span class="pln"><br /> </span><span class="pun">│</span><span class="pln"><br /> </span><span class="pun">∨</span><span class="pln"><br /></span><span class="pun">╔════════════════╗</span><span class="pln"><br /></span><span class="pun">║</span><span class="pln"> business rules </span><span class="pun">║─────></span><span class="pln"> </span><span class="str">"rejected"</span><span class="pln"><br /></span><span class="pun">╚════════════════╝</span><span class="pln"><br /> </span><span class="pun">│</span><span class="pln"><br /> </span><span class="pun">│</span><span class="pln"><br /> </span><span class="pun">│</span><span class="pln"><br /> </span><span class="pun">∨</span><span class="pln"><br /> </span><span class="pun">╔════════╗</span><span class="pln"><br /> </span><span class="pun">║</span><span class="pln"> commit </span><span class="pun">║</span><span class="pln"><br /> </span><span class="pun">╚════════╝</span></code></pre></div></div></div></div></div><div class="row-fluid"><div class="span12"><h2>Usage</h2><div class="row-fluid"><div class="span4"><h4 class="feature-title">Configure</h4></div><div class="span8"><div class="feature"><pre><code><span class="kwd">var</span><span class="pln"> domain </span><span class="pun">=</span><span class="pln"> </span><span class="kwd">require</span><span class="pun">(</span><span class="str">'cqrs-domain'</span><span class="pun">)({</span><span class="pln"><br /> </span><span class="com">// the path to the "working directory"</span><span class="pln"><br /> </span><span class="com">// can be structured like</span><span class="pln"><br /> </span><span class="com">// [set 1](https://github.com/adrai/node-cqrs-domain/tree/master/test/integration/fixture/set1) or</span><span class="pln"><br /> </span><span class="com">// [set 2](https://github.com/adrai/node-cqrs-domain/tree/master/test/integration/fixture/set2)</span><span class="pln"><br /> domainPath</span><span class="pun">:</span><span class="pln"> </span><span class="str">'/path/to/my/files'</span><span class="pun">,</span><span class="pln"><br /> <br /> </span><span class="com">// optional, default is 'commandRejected'</span><span class="pln"><br /> </span><span class="com">// will be used if an error occurs and an event should be generated</span><span class="pln"><br /> commandRejectedEventName</span><span class="pun">:</span><span class="pln"> </span><span class="str">'rejectedCommand'</span><span class="pun">,</span><span class="pln"><br /> <br /> </span><span class="com">// optional, default is 800</span><span class="pln"><br /> </span><span class="com">// if using in scaled systems and not guaranteeing that each command for an aggregate instance</span><span class="pln"><br /> </span><span class="com">// dispatches to the same worker process, this module tries to catch the concurrency issues and</span><span class="pln"><br /> </span><span class="com">// retries to handle the command after a timeout between 0 and the defined value</span><span class="pln"><br /> retryOnConcurrencyTimeout</span><span class="pun">:</span><span class="pln"> </span><span class="lit">1000</span><span class="pun">,</span><span class="pln"><br /> <br /> </span><span class="com">// optional, default is 100</span><span class="pln"><br /> </span><span class="com">// global snapshot threshold value for all aggregates</span><span class="pln"><br /> </span><span class="com">// defines the amount of loaded events, if there are more events to load, it will do a snapshot, so next loading is faster</span><span class="pln"><br /> </span><span class="com">// an individual snapshot threshold defining algorithm can be defined per aggregate (scroll down)</span><span class="pln"><br /> snapshotThreshold</span><span class="pun">:</span><span class="pln"> </span><span class="lit">1000</span><span class="pun">,</span><span class="pln"><br /> <br /> </span><span class="com">// optional, default is in-memory</span><span class="pln"><br /> </span><span class="com">// currently supports: mongodb, redis, tingodb, azuretable and inmemory</span><span class="pln"><br /> </span><span class="com">// hint: [eventstore](https://github.com/adrai/node-eventstore#provide-implementation-for-storage)</span><span class="pln"><br /> eventStore</span><span class="pun">:</span><span class="pln"> </span><span class="pun">{</span><span class="pln"><br /> type</span><span class="pun">:</span><span class="pln"> </span><span class="str">'mongodb'</span><span class="pun">,</span><span class="pln"><br /> host</span><span class="pun">:</span><span class="pln"> </span><span class="str">'localhost'</span><span class="pun">,</span><span class="pln"> </span><span class="com">// optional</span><span class="pln"><br /> port</span><span class="pun">:</span><span class="pln"> </span><span class="lit">27017</span><span class="pun">,</span><span class="pln"> </span><span class="com">// optional</span><span class="pln"><br /> dbName</span><span class="pun">:</span><span class="pln"> </span><span class="str">'domain'</span><span class="pun">,</span><span class="pln"> </span><span class="com">// optional</span><span class="pln"><br /> eventsCollectionName</span><span class="pun">:</span><span class="pln"> </span><span class="str">'events'</span><span class="pun">,</span><span class="pln"> </span><span class="com">// optional</span><span class="pln"><br /> snapshotsCollectionName</span><span class="pun">:</span><span class="pln"> </span><span class="str">'snapshots'</span><span class="pun">,</span><span class="pln"> </span><span class="com">// optional</span><span class="pln"><br /> transactionsCollectionName</span><span class="pun">:</span><span class="pln"> </span><span class="str">'transactions'</span><span class="pun">,</span><span class="pln"> </span><span class="com">// optional</span><span class="pln"><br /> timeout</span><span class="pun">:</span><span class="pln"> </span><span class="lit">10000</span><span class="pln"> </span><span class="com">// optional</span><span class="pln"><br /> </span><span class="com">// authSource: 'authedicationDatabase', // optional</span><span class="pln"><br /> </span><span class="com">// username: 'technicalDbUser', // optional</span><span class="pln"><br /> </span><span class="com">// password: 'secret' // optional</span><span class="pln"><br /> </span><span class="com">// url: 'mongodb://user:pass@host:port/db?opts // optional</span><span class="pln"><br /> </span><span class="pun">},</span><span class="pln"><br /> <br /> </span><span class="com">// optional, default is in-memory</span><span class="pln"><br /> </span><span class="com">// currently supports: mongodb, redis, tingodb, couchdb, azuretable and inmemory</span><span class="pln"><br /> </span><span class="com">// hint settings like: [eventstore](https://github.com/adrai/node-eventstore#provide-implementation-for-storage)</span><span class="pln"><br /> aggregateLock</span><span class="pun">:</span><span class="pln"> </span><span class="pun">{</span><span class="pln"><br /> type</span><span class="pun">:</span><span class="pln"> </span><span class="str">'redis'</span><span class="pun">,</span><span class="pln"><br /> host</span><span class="pun">:</span><span class="pln"> </span><span class="str">'localhost'</span><span class="pun">,</span><span class="pln"> </span><span class="com">// optional</span><span class="pln"><br /> port</span><span class="pun">:</span><span class="pln"> </span><span class="lit">6379</span><span class="pun">,</span><span class="pln"> </span><span class="com">// optional</span><span class="pln"><br /> db</span><span class="pun">:</span><span class="pln"> </span><span class="lit">0</span><span class="pun">,</span><span class="pln"> </span><span class="com">// optional</span><span class="pln"><br /> prefix</span><span class="pun">:</span><span class="pln"> </span><span class="str">'domain_aggregate_lock'</span><span class="pun">,</span><span class="pln"> </span><span class="com">// optional</span><span class="pln"><br /> timeout</span><span class="pun">:</span><span class="pln"> </span><span class="lit">10000</span><span class="pln"> </span><span class="com">// optional</span><span class="pln"><br /> </span><span class="com">// password: 'secret' // optional</span><span class="pln"><br /> </span><span class="pun">},</span><span class="pln"><br /> <br /> </span><span class="com">// optional, default is not set</span><span class="pln"><br /> </span><span class="com">// checks if command was already seen in the last time -> ttl</span><span class="pln"><br /> </span><span class="com">// currently supports: mongodb, redis, tingodb and inmemory</span><span class="pln"><br /> </span><span class="com">// hint settings like: [eventstore](https://github.com/adrai/node-eventstore#provide-implementation-for-storage)</span><span class="pln"><br /> deduplication</span><span class="pun">:</span><span class="pln"> </span><span class="pun">{</span><span class="pln"><br /> type</span><span class="pun">:</span><span class="pln"> </span><span class="str">'redis'</span><span class="pun">,</span><span class="pln"><br /> ttl</span><span class="pun">:</span><span class="pln"> </span><span class="lit">1000</span><span class="pln"> </span><span class="pun">*</span><span class="pln"> </span><span class="lit">60</span><span class="pln"> </span><span class="pun">*</span><span class="pln"> </span><span class="lit">60</span><span class="pln"> </span><span class="pun">*</span><span class="pln"> </span><span class="lit">1</span><span class="pun">,</span><span class="pln"> </span><span class="com">// 1 hour // optional</span><span class="pln"><br /> host</span><span class="pun">:</span><span class="pln"> </span><span class="str">'localhost'</span><span class="pun">,</span><span class="pln"> </span><span class="com">// optional</span><span class="pln"><br /> port</span><span class="pun">:</span><span class="pln"> </span><span class="lit">6379</span><span class="pun">,</span><span class="pln"> </span><span class="com">// optional</span><span class="pln"><br /> db</span><span class="pun">:</span><span class="pln"> </span><span class="lit">0</span><span class="pun">,</span><span class="pln"> </span><span class="com">// optional</span><span class="pln"><br /> prefix</span><span class="pun">:</span><span class="pln"> </span><span class="str">'domain_aggregate_lock'</span><span class="pun">,</span><span class="pln"> </span><span class="com">// optional</span><span class="pln"><br /> timeout</span><span class="pun">:</span><span class="pln"> </span><span class="lit">10000</span><span class="pln"> </span><span class="com">// optional</span><span class="pln"><br /> </span><span class="com">// password: 'secret' // optional</span><span class="pln"><br /> </span><span class="pun">}</span><span class="pln"><br /></span><span class="pun">});</span></code></pre></div></div></div><div class="row-fluid"><div class="span4"><h4 class="feature-title">Using factory methods for event store or / and aggregate lock in domain definition</h4><p>You can replace the framework-provided implementation of event store or / and aggregate lock with the one of your own. To do that, you need to include a factory method in the options object passed to the domain constructor. Using the factory methods, the example above might become:</p></div><div class="span8"><div class="feature"><pre><code><span class="kwd">var</span><span class="pln"> myGetEventStore </span><span class="pun">=</span><span class="pln"> </span><span class="kwd">require</span><span class="pun">(</span><span class="str">'./getEventStore.js'</span><span class="pun">);</span><span class="pln"><br /></span><span class="kwd">var</span><span class="pln"> myLock </span><span class="pun">=</span><span class="pln"> </span><span class="kwd">require</span><span class="pun">(</span><span class="str">'./myLock.js'</span><span class="pun">);</span><span class="pln"><br /> <br /></span><span class="kwd">var</span><span class="pln"> domain </span><span class="pun">=</span><span class="pln"> </span><span class="kwd">require</span><span class="pun">(</span><span class="str">'cqrs-domain'</span><span class="pun">)({</span><span class="pln"><br /> domainPath</span><span class="pun">:</span><span class="pln"> </span><span class="str">'/path/to/my/files'</span><span class="pun">,</span><span class="pln"><br /> commandRejectedEventName</span><span class="pun">:</span><span class="pln"> </span><span class="str">'rejectedCommand'</span><span class="pun">,</span><span class="pln"><br /> retryOnConcurrencyTimeout</span><span class="pun">:</span><span class="pln"> </span><span class="lit">1000</span><span class="pun">,</span><span class="pln"><br /> snapshotThreshold</span><span class="pun">:</span><span class="pln"> </span><span class="lit">1000</span><span class="pun">,</span><span class="pln"><br /> <br /> eventStore</span><span class="pun">:</span><span class="pln"> </span><span class="kwd">function</span><span class="pln"> </span><span class="pun">()</span><span class="pln"> </span><span class="pun">{</span><span class="pln"><br /> </span><span class="kwd">return</span><span class="pln"> myGetEventStore</span><span class="pun">({</span><span class="pln"><br /> host</span><span class="pun">:</span><span class="pln"> </span><span class="str">'127.0.0.1'</span><span class="pun">,</span><span class="pln"><br /> port</span><span class="pun">:</span><span class="pln"> </span><span class="lit">2113</span><span class="pun">,</span><span class="pln"><br /> username</span><span class="pun">:</span><span class="pln"> </span><span class="str">'admin'</span><span class="pun">,</span><span class="pln"><br /> password</span><span class="pun">:</span><span class="pln"> </span><span class="str">'changeit'</span><span class="pln"><br /> </span><span class="pun">});</span><span class="pln"><br /> </span><span class="pun">},</span><span class="pln"><br /> <br /> aggregateLock</span><span class="pun">:</span><span class="pln"> </span><span class="pun">:</span><span class="pln"> </span><span class="kwd">function</span><span class="pln"> </span><span class="pun">()</span><span class="pln"> </span><span class="pun">{</span><span class="pln"><br /> </span><span class="kwd">return</span><span class="pln"> myLock</span><span class="pun">({</span><span class="pln"><br /> </span><span class="com">// ....</span><span class="pln"><br /> </span><span class="pun">});</span><span class="pln"><br /> </span><span class="pun">},</span><span class="pln"><br /> <br /> deduplication</span><span class="pun">:</span><span class="pln"> </span><span class="pun">:</span><span class="pln"> </span><span class="kwd">function</span><span class="pln"> </span><span class="pun">()</span><span class="pln"> </span><span class="pun">{</span><span class="pln"><br /> </span><span class="kwd">return</span><span class="pln"> myCommandBumper</span><span class="pun">({</span><span class="pln"><br /> </span><span class="com">// ....</span><span class="pln"><br /> </span><span class="pun">});</span><span class="pln"><br /> </span><span class="pun">}</span><span class="pln"><br /></span><span class="pun">});</span></code></pre></div><p>When using factory methods, the objects they return are required to implement the following public interfaces:</p><div class="feature"><pre><code><span class="typ">Event</span><span class="pln"> </span><span class="typ">Store</span><span class="pun">:</span><span class="pln"><br /> <br /> f</span><span class="pun">:</span><span class="pln"> init</span><span class="pun">(</span><span class="kwd">function</span><span class="pun">(</span><span class="pln">err</span><span class="pun">));</span><span class="pln"><br /> f</span><span class="pun">:</span><span class="pln"> getNewId</span><span class="pun">(</span><span class="kwd">function</span><span class="pln"> </span><span class="pun">(</span><span class="pln">err</span><span class="pun">,</span><span class="pln"> id</span><span class="pun">));</span><span class="pln"><br /> f</span><span class="pun">:</span><span class="pln"> on</span><span class="pun">(</span><span class="pln">evtName</span><span class="pun">,</span><span class="pln"> </span><span class="kwd">function</span><span class="pln"> </span><span class="pun">(</span><span class="pln">err</span><span class="pun">));</span><span class="pln"><br /> f</span><span class="pun">:</span><span class="pln"> getFromSnapshot</span><span class="pun">(</span><span class="pln">query</span><span class="pun">,</span><span class="pln"> revMax</span><span class="pun">,</span><span class="pln"> </span><span class="kwd">function</span><span class="pun">(</span><span class="pln">err</span><span class="pun">,</span><span class="pln"> snapshot</span><span class="pun">,</span><span class="pln"> stream</span><span class="pun">));</span><span class="pln"><br /> f</span><span class="pun">:</span><span class="pln"> createSnapshot</span><span class="pun">(</span><span class="pln">obj</span><span class="pun">,</span><span class="pln"> </span><span class="kwd">function</span><span class="pln"> </span><span class="pun">(</span><span class="pln">err</span><span class="pun">));</span><span class="pln"><br /> f</span><span class="pun">:</span><span class="pln"> setEventToDispatched</span><span class="pun">(</span><span class="pln">evt</span><span class="pun">,</span><span class="pln"> </span><span class="kwd">function</span><span class="pln"> </span><span class="pun">(</span><span class="pln">err</span><span class="pun">));</span><span class="pln"><br /> <br /></span><span class="typ">Event</span><span class="pln"> </span><span class="typ">Stream</span><span class="pln"> </span><span class="pun">(</span><span class="pln">returned </span><span class="kwd">by</span><span class="pln"> getFromSnapshot through the callback</span><span class="pun">):</span><span class="pln"><br /> <br /> p</span><span class="pun">:</span><span class="pln"> events<br /> p</span><span class="pun">:</span><span class="pln"> lastRevision<br /> p</span><span class="pun">:</span><span class="pln"> eventsToDispatch<br /> f</span><span class="pun">:</span><span class="pln"> addEvents</span><span class="pun">(</span><span class="pln">evts</span><span class="pun">)</span><span class="pln"><br /> f</span><span class="pun">:</span><span class="pln"> commit</span><span class="pun">(</span><span class="kwd">function</span><span class="pln"> </span><span class="pun">(</span><span class="pln">err</span><span class="pun">,</span><span class="pln"> stream</span><span class="pun">));</span><span class="pln"><br /> <br /></span><span class="typ">Aggregate</span><span class="pln"> </span><span class="typ">Lock</span><span class="pun">:</span><span class="pln"><br /> <br /> f</span><span class="pun">:</span><span class="pln"> connect</span><span class="pun">(</span><span class="kwd">function</span><span class="pun">(</span><span class="pln">err</span><span class="pun">,</span><span class="pln"> </span><span class="kwd">lock</span><span class="pun">))</span><span class="pln"><br /> f</span><span class="pun">:</span><span class="pln"> disconnect</span><span class="pun">(</span><span class="kwd">function</span><span class="pun">(</span><span class="pln">err</span><span class="pun">))</span><span class="pln"><br /> f</span><span class="pun">:</span><span class="pln"> getNewId</span><span class="pun">(</span><span class="kwd">function</span><span class="pun">(</span><span class="pln">err</span><span class="pun">,</span><span class="pln"> id</span><span class="pun">))</span><span class="pln"><br /> f</span><span class="pun">:</span><span class="pln"> reserve</span><span class="pun">(</span><span class="pln">workerId</span><span class="pun">,</span><span class="pln"> aggregateId</span><span class="pun">,</span><span class="pln"> </span><span class="kwd">function</span><span class="pun">(</span><span class="pln">err</span><span class="pun">))</span><span class="pln"><br /> f</span><span class="pun">:</span><span class="pln"> getAll</span><span class="pun">(</span><span class="pln">aggregateId</span><span class="pun">,</span><span class="pln"> </span><span class="kwd">function</span><span class="pun">(</span><span class="pln">err</span><span class="pun">,</span><span class="pln"> workerIds</span><span class="pun">))</span><span class="pln"><br /> f</span><span class="pun">:</span><span class="pln"> resolve</span><span class="pun">(</span><span class="pln">aggregateId</span><span class="pun">,</span><span class="pln"> </span><span class="kwd">function</span><span class="pun">(</span><span class="pln">err</span><span class="pun">))</span><span class="pln"><br /> <br /></span><span class="typ">Command</span><span class="pln"> </span><span class="typ">Bumper</span><span class="pun">:</span><span class="pln"><br /> <br /> f</span><span class="pun">:</span><span class="pln"> connect</span><span class="pun">(</span><span class="kwd">function</span><span class="pun">(</span><span class="pln">err</span><span class="pun">,</span><span class="pln"> </span><span class="kwd">lock</span><span class="pun">))</span><span class="pln"><br /> f</span><span class="pun">:</span><span class="pln"> disconnect</span><span class="pun">(</span><span class="kwd">function</span><span class="pun">(</span><span class="pln">err</span><span class="pun">))</span><span class="pln"><br /> f</span><span class="pun">:</span><span class="pln"> getNewId</span><span class="pun">(</span><span class="kwd">function</span><span class="pun">(</span><span class="pln">err</span><span class="pun">,</span><span class="pln"> id</span><span class="pun">))</span><span class="pln"><br /> f</span><span class="pun">:</span><span class="pln"> add</span><span class="pun">(</span><span class="pln">key</span><span class="pun">,</span><span class="pln"> ttl</span><span class="pun">,</span><span class="pln"> </span><span class="kwd">function</span><span class="pun">(</span><span class="pln">err</span><span class="pun">,</span><span class="pln"> added</span><span class="pun">))</span><span class="pln"><br /> <br /></span><span class="kwd">where</span><span class="pun">:</span><span class="pln"><br /> <br /> f</span><span class="pun">:</span><span class="pln"> </span><span class="kwd">function</span><span class="pln"><br /> p</span><span class="pun">:</span><span class="pln"> property</span></code></pre></div></div></div><div class="row-fluid"><div class="span4"><h4 class="feature-title">Exposed errors</h4><p>You can use this for example in you custom command handlers.</p></div><div class="span8"><div class="feature"><pre><code><span class="kwd">require</span><span class="pun">(</span><span class="str">'cqrs-domain'</span><span class="pun">).</span><span class="pln">errors</span><span class="pun">.</span><span class="typ">ValidationError</span><span class="pln"><br /></span><span class="kwd">require</span><span class="pun">(</span><span class="str">'cqrs-domain'</span><span class="pun">).</span><span class="pln">errors</span><span class="pun">.</span><span class="typ">BusinessRuleError</span><span class="pln"><br /></span><span class="kwd">require</span><span class="pun">(</span><span class="str">'cqrs-domain'</span><span class="pun">).</span><span class="pln">errors</span><span class="pun">.</span><span class="typ">AggregateDestroyedError</span><span class="pln"><br /></span><span class="kwd">require</span><span class="pun">(</span><span class="str">'cqrs-domain'</span><span class="pun">).</span><span class="pln">errors</span><span class="pun">.</span><span class="typ">AggregateConcurrencyError</span><span class="pln"><br /></span><span class="kwd">require</span><span class="pun">(</span><span class="str">'cqrs-domain'</span><span class="pun">).</span><span class="pln">errors</span><span class="pun">.</span><span class="typ">ConcurrencyError</span><span class="pln"><br /></span><span class="kwd">require</span><span class="pun">(</span><span class="str">'cqrs-domain'</span><span class="pun">).</span><span class="pln">errors</span><span class="pun">.</span><span class="typ">DuplicateCommandError</span></code></pre></div></div></div><div class="row-fluid"><div class="span4"><h4 class="feature-title">Catch connect and disconnect events</h4></div><div class="span8"><div class="feature"><pre><code><span class="com">// eventStore</span><span class="pln"><br />domain</span><span class="pun">.</span><span class="pln">eventStore</span><span class="pun">.</span><span class="pln">on</span><span class="pun">(</span><span class="str">'connect'</span><span class="pun">,</span><span class="pln"> </span><span class="kwd">function</span><span class="pun">()</span><span class="pln"> </span><span class="pun">{</span><span class="pln"><br /> console</span><span class="pun">.</span><span class="pln">log</span><span class="pun">(</span><span class="str">'eventStore connected'</span><span class="pun">);</span><span class="pln"><br /></span><span class="pun">});</span><span class="pln"><br /> <br />domain</span><span class="pun">.</span><span class="pln">eventStore</span><span class="pun">.</span><span class="pln">on</span><span class="pun">(</span><span class="str">'disconnect'</span><span class="pun">,</span><span class="pln"> </span><span class="kwd">function</span><span class="pun">()</span><span class="pln"> </span><span class="pun">{</span><span class="pln"><br /> console</span><span class="pun">.</span><span class="pln">log</span><span class="pun">(</span><span class="str">'eventStore disconnected'</span><span class="pun">);</span><span class="pln"><br /></span><span class="pun">});</span><span class="pln"><br /> <br /></span><span class="com">// aggregateLock</span><span class="pln"><br />domain</span><span class="pun">.</span><span class="pln">aggregateLock</span><span class="pun">.</span><span class="pln">on</span><span class="pun">(</span><span class="str">'connect'</span><span class="pun">,</span><span class="pln"> </span><span class="kwd">function</span><span class="pun">()</span><span class="pln"> </span><span class="pun">{</span><span class="pln"><br /> console</span><span class="pun">.</span><span class="pln">log</span><span class="pun">(</span><span class="str">'aggregateLock connected'</span><span class="pun">);</span><span class="pln"><br /></span><span class="pun">});</span><span class="pln"><br /> <br />domain</span><span class="pun">.</span><span class="pln">aggregateLock</span><span class="pun">.</span><span class="pln">on</span><span class="pun">(</span><span class="str">'disconnect'</span><span class="pun">,</span><span class="pln"> </span><span class="kwd">function</span><span class="pun">()</span><span class="pln"> </span><span class="pun">{</span><span class="pln"><br /> console</span><span class="pun">.</span><span class="pln">log</span><span class="pun">(</span><span class="str">'aggregateLock disconnected'</span><span class="pun">);</span><span class="pln"><br /></span><span class="pun">});</span><span class="pln"><br /> <br /></span><span class="com">// commandBumper</span><span class="pln"><br />domain</span><span class="pun">.</span><span class="pln">commandBumper</span><span class="pun">.</span><span class="pln">on</span><span class="pun">(</span><span class="str">'connect'</span><span class="pun">,</span><span class="pln"> </span><span class="kwd">function</span><span class="pun">()</span><span class="pln"> </span><span class="pun">{</span><span class="pln"><br /> console</span><span class="pun">.</span><span class="pln">log</span><span class="pun">(</span><span class="str">'commandBumper connected'</span><span class="pun">);</span><span class="pln"><br /></span><span class="pun">});</span><span class="pln"><br /> <br />domain</span><span class="pun">.</span><span class="pln">commandBumper</span><span class="pun">.</span><span class="pln">on</span><span class="pun">(</span><span class="str">'disconnect'</span><span class="pun">,</span><span class="pln"> </span><span class="kwd">function</span><span class="pun">()</span><span class="pln"> </span><span class="pun">{</span><span class="pln"><br /> console</span><span class="pun">.</span><span class="pln">log</span><span class="pun">(</span><span class="str">'commandBumper disconnected'</span><span class="pun">);</span><span class="pln"><br /></span><span class="pun">});</span><span class="pln"><br /> <br /> <br /></span><span class="com">// anything (eventStore or aggregateLock or commandBumper)</span><span class="pln"><br />domain</span><span class="pun">.</span><span class="pln">on</span><span class="pun">(</span><span class="str">'connect'</span><span class="pun">,</span><span class="pln"> </span><span class="kwd">function</span><span class="pun">()</span><span class="pln"> </span><span class="pun">{</span><span class="pln"><br /> console</span><span class="pun">.</span><span class="pln">log</span><span class="pun">(</span><span class="str">'something connected'</span><span class="pun">);</span><span class="pln"><br /></span><span class="pun">});</span><span class="pln"><br /> <br />domain</span><span class="pun">.</span><span class="pln">on</span><span class="pun">(</span><span class="str">'disconnect'</span><span class="pun">,</span><span class="pln"> </span><span class="kwd">function</span><span class="pun">()</span><span class="pln"> </span><span class="pun">{</span><span class="pln"><br /> console</span><span class="pun">.</span><span class="pln">log</span><span class="pun">(</span><span class="str">'something disconnected'</span><span class="pun">);</span><span class="pln"><br /></span><span class="pun">});</span></code></pre><div class="alert alert-info feature-description"><h6>Important hint:</h6><p>For example in a cloud environment the disconnect event could be used to kill the process.</p></div></div></div></div><div class="row-fluid"><div class="span4"><h4 class="feature-title">Define the command structure</h4><p>The values describes the path to that property in the command message.</p></div><div class="span8"><div class="feature"><pre><code><span class="pln">domain</span><span class="pun">.</span><span class="pln">defineCommand</span><span class="pun">({</span><span class="pln"><br /> </span><span class="com">// optional, default is 'id'</span><span class="pln"><br /> id</span><span class="pun">:</span><span class="pln"> </span><span class="str">'id'</span><span class="pun">,</span><span class="pln"><br /> <br /> </span><span class="com">// optional, default is 'name'</span><span class="pln"><br /> name</span><span class="pun">:</span><span class="pln"> </span><span class="str">'name'</span><span class="pun">,</span><span class="pln"><br /> <br /> </span><span class="com">// optional, default is 'aggregate.id'</span><span class="pln"><br /> </span><span class="com">// if an aggregate id is not defined in the command, the command handler will create a new aggregate instance</span><span class="pln"><br /> aggregateId</span><span class="pun">:</span><span class="pln"> </span><span class="str">'aggregate.id'</span><span class="pun">,</span><span class="pln"><br /> <br /> </span><span class="com">// optional, only makes sense if contexts are defined in the 'domainPath' structure</span><span class="pln"><br /> context</span><span class="pun">:</span><span class="pln"> </span><span class="str">'context.name'</span><span class="pun">,</span><span class="pln"><br /> <br /> </span><span class="com">// optional, only makes sense if aggregates with names are defined in the 'domainPath' structure</span><span class="pln"><br /> aggregate</span><span class="pun">:</span><span class="pln"> </span><span class="str">'aggregate.name'</span><span class="pun">,</span><span class="pln"><br /> <br /> </span><span class="com">// optional, but recommended</span><span class="pln"><br /> payload</span><span class="pun">:</span><span class="pln"> </span><span class="str">'payload'</span><span class="pun">,</span><span class="pln"><br /> <br /> </span><span class="com">// optional, if defined the command handler will check if the command can be handled</span><span class="pln"><br /> </span><span class="com">// if you want the command to be handled in a secure/transactional way pass a revision value that matches the current aggregate revision</span><span class="pln"><br /> revision</span><span class="pun">:</span><span class="pln"> </span><span class="str">'revision'</span><span class="pun">,</span><span class="pln"><br /> <br /> </span><span class="com">// optional, if defined the command handler will search for a handle that matches command name and version number</span><span class="pln"><br /> version</span><span class="pun">:</span><span class="pln"> </span><span class="str">'version'</span><span class="pun">,</span><span class="pln"><br /> <br /> </span><span class="com">// optional, if defined theses values will be copied to the event (can be used to transport information like userId, etc..)</span><span class="pln"><br /> meta</span><span class="pun">:</span><span class="pln"> </span><span class="str">'meta'</span><span class="pln"><br /></span><span class="pun">});</span></code></pre></div></div></div><div class="row-fluid"><div class="span4"><h4 class="feature-title">Define the event structure</h4><p>The values describes the path to that property in the event message.</p></div><div class="span8"><div class="feature"><pre><code><span class="pln">domain</span><span class="pun">.</span><span class="pln">defineEvent</span><span class="pun">({</span><span class="pln"><br /> </span><span class="com">// optional, default is 'correlationId'</span><span class="pln"><br /> </span><span class="com">// will use the command id as correlationId, so you can match it in the sender</span><span class="pln"><br /> correlationId</span><span class="pun">:</span><span class="pln"> </span><span class="str">'correlationId'</span><span class="pun">,</span><span class="pln"><br /> <br /> </span><span class="com">// optional, default is 'id'</span><span class="pln"><br /> id</span><span class="pun">:</span><span class="pln"> </span><span class="str">'id'</span><span class="pun">,</span><span class="pln"><br /> <br /> </span><span class="com">// optional, default is 'name'</span><span class="pln"><br /> name</span><span class="pun">:</span><span class="pln"> </span><span class="str">'name'</span><span class="pun">,</span><span class="pln"><br /> <br /> </span><span class="com">// optional, default is 'aggregate.id'</span><span class="pln"><br /> aggregateId</span><span class="pun">:</span><span class="pln"> </span><span class="str">'aggregate.id'</span><span class="pun">,</span><span class="pln"><br /> <br /> </span><span class="com">// optional, only makes sense if contexts are defined in the 'domainPath' structure</span><span class="pln"><br /> context</span><span class="pun">:</span><span class="pln"> </span><span class="str">'context.name'</span><span class="pun">,</span><span class="pln"><br /> <br /> </span><span class="com">// optional, only makes sense if aggregates with names are defined in the 'domainPath' structure</span><span class="pln"><br /> aggregate</span><span class="pun">:</span><span class="pln"> </span><span class="str">'aggregate.name'</span><span class="pun">,</span><span class="pln"><br /> <br /> </span><span class="com">// optional, default is 'payload'</span><span class="pln"><br /> payload</span><span class="pun">:</span><span class="pln"> </span><span class="str">'payload'</span><span class="pun">,</span><span class="pln"><br /> <br /> </span><span class="com">// optional, default is 'revision'</span><span class="pln"><br /> </span><span class="com">// will represent the aggregate revision, can be used in next command</span><span class="pln"><br /> revision</span><span class="pun">:</span><span class="pln"> </span><span class="str">'revision'</span><span class="pun">,</span><span class="pln"><br /> <br /> </span><span class="com">// optional</span><span class="pln"><br /> version</span><span class="pun">:</span><span class="pln"> </span><span class="str">'version'</span><span class="pun">,</span><span class="pln"><br /> <br /> </span><span class="com">// optional, if defined the values of the command will be copied to the event (can be used to transport information like userId, etc..)</span><span class="pln"><br /> meta</span><span class="pun">:</span><span class="pln"> </span><span class="str">'meta'</span><span class="pun">,</span><span class="pln"><br /> <br /> </span><span class="com">// optional, if defined the commit date of the eventstore will be saved in this value</span><span class="pln"><br /> commitStamp</span><span class="pun">:</span><span class="pln"> </span><span class="str">'commitStamp'</span><span class="pln"><br /></span><span class="pun">});</span></code></pre></div></div></div><div class="row-fluid"><div class="span4"><h4 class="feature-title">Define the id generator function [optional]</h4><p>you can define a synchronous function</p></div><div class="span8"><div class="feature"><pre><code><span class="pln">domain</span><span class="pun">.</span><span class="pln">idGenerator</span><span class="pun">(</span><span class="kwd">function</span><span class="pln"> </span><span class="pun">()</span><span class="pln"> </span><span class="pun">{</span><span class="pln"><br /> </span><span class="kwd">var</span><span class="pln"> id </span><span class="pun">=</span><span class="pln"> </span><span class="kwd">require</span><span class="pun">(</span><span class="str">'node-uuid'</span><span class="pun">).</span><span class="pln">v4</span><span class="pun">().</span><span class="pln">toString</span><span class="pun">();</span><span class="pln"><br /> </span><span class="kwd">return</span><span class="pln"> id</span><span class="pun">;</span><span class="pln"><br /></span><span class="pun">});</span></code></pre></div></div></div><div class="row-fluid"><div class="span4"><p>or you can define an asynchronous function</p></div><div class="span8"><div class="feature"><pre><code><span class="pln">domain</span><span class="pun">.</span><span class="pln">idGenerator</span><span class="pun">(</span><span class="kwd">function</span><span class="pln"> </span><span class="pun">(</span><span class="pln">callback</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln"><br /> setTimeout</span><span class="pun">(</span><span class="kwd">function</span><span class="pln"> </span><span class="pun">()</span><span class="pln"> </span><span class="pun">{</span><span class="pln"><br /> </span><span class="kwd">var</span><span class="pln"> id </span><span class="pun">=</span><span class="pln"> </span><span class="kwd">require</span><span class="pun">(</span><span class="str">'node-uuid'</span><span class="pun">).</span><span class="pln">v4</span><span class="pun">().</span><span class="pln">toString</span><span class="pun">();</span><span class="pln"><br /> callback</span><span class="pun">(</span><span class="kwd">null</span><span class="pun">,</span><span class="pln"> id</span><span class="pun">);</span><span class="pln"><br /> </span><span class="pun">},</span><span class="pln"> </span><span class="lit">50</span><span class="pun">);</span><span class="pln"><br /></span><span class="pun">});</span></code></pre></div></div></div><div class="row-fluid"><div class="span4"><h4 class="feature-title">Define the aggregate id generator function [optional]</h4><p>you can define a synchronous function</p></div><div class="span8"><div class="feature"><pre><code><span class="pln">domain</span><span class="pun">.</span><span class="pln">aggregateIdGenerator</span><span class="pun">(</span><span class="kwd">function</span><span class="pln"> </span><span class="pun">()</span><span class="pln"> </span><span class="pun">{</span><span class="pln"><br /> </span><span class="kwd">var</span><span class="pln"> id </span><span class="pun">=</span><span class="pln"> </span><span class="kwd">require</span><span class="pun">(</span><span class="str">'node-uuid'</span><span class="pun">).</span><span class="pln">v4</span><span class="pun">().</span><span class="pln">toString</span><span class="pun">();</span><span class="pln"><br /> </span><span class="kwd">return</span><span class="pln"> id</span><span class="pun">;</span><span class="pln"><br /></span><span class="pun">});</span></code></pre></div></div></div><div class="row-fluid"><div class="span4"><p>or you can define an asynchronous function</p></div><div class="span8"><div class="feature"><pre><code><span class="pln">domain</span><span class="pun">.</span><span class="pln">aggregateIdGenerator</span><span class="pun">(</span><span class="kwd">function</span><span class="pln"> </span><span class="pun">(</span><span class="pln">callback</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln"><br /> setTimeout</span><span class="pun">(</span><span class="kwd">function</span><span class="pln"> </span><span class="pun">()</span><span class="pln"> </span><span class="pun">{</span><span class="pln"><br /> </span><span class="kwd">var</span><span class="pln"> id </span><span class="pun">=</span><span class="pln"> </span><span class="kwd">require</span><span class="pun">(</span><span class="str">'node-uuid'</span><span class="pun">).</span><span class="pln">v4</span><span class="pun">().</span><span class="pln">toString</span><span class="pun">();</span><span class="pln"><br /> callback</span><span class="pun">(</span><span class="kwd">null</span><span class="pun">,</span><span class="pln"> id</span><span class="pun">);</span><span class="pln"><br /> </span><span class="pun">},</span><span class="pln"> </span><span class="lit">50</span><span class="pun">);</span><span class="pln"><br /></span><span class="pun">});</span></code></pre></div></div></div><div class="row-fluid"><div class="span4"><h4 class="feature-title">Wire up events [optional]</h4><p>you can define a synchronous function</p></div><div class="span8"><div class="feature"><pre><code><span class="com">// pass events to bus</span><span class="pln"><br />domain</span><span class="pun">.</span><span class="pln">onEvent</span><span class="pun">(</span><span class="kwd">function</span><span class="pln"> </span><span class="pun">(</span><span class="pln">evt</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln"><br /> bus</span><span class="pun">.</span><span class="pln">emit</span><span class="pun">(</span><span class="str">'event'</span><span class="pun">,</span><span class="pln"> evt</span><span class="pun">);</span><span class="pln"><br /></span><span class="pun">});</span></code></pre></div></div></div><div class="row-fluid"><div class="span4"><p>or you can define an asynchronous function</p></div><div class="span8"><div class="feature"><pre><code><span class="com">// pass events to bus</span><span class="pln"><br />domain</span><span class="pun">.</span><span class="pln">onEvent</span><span class="pun">(</span><span class="kwd">function</span><span class="pln"> </span><span class="pun">(</span><span class="pln">evt</span><span class="pun">,</span><span class="pln"> callback</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln"><br /> bus</span><span class="pun">.</span><span class="pln">emit</span><span class="pun">(</span><span class="str">'event'</span><span class="pun">,</span><span class="pln"> evt</span><span class="pun">,</span><span class="pln"> </span><span class="kwd">function</span><span class="pln"> ack </span><span class="pun">()</span><span class="pln"> </span><span class="pun">{</span><span class="pln"><br /> callback</span><span class="pun">();</span><span class="pln"><br /> </span><span class="pun">});</span><span class="pln"><br /></span><span class="pun">});</span></code></pre></div></div></div><div class="row-fluid"><div class="span4"><h4 class="feature-title">Initialization</h4></div><div class="span8"><div class="feature"><pre><code><span class="pln">domain</span><span class="pun">.</span><span class="pln">init</span><span class="pun">(</span><span class="kwd">function</span><span class="pln"> </span><span class="pun">(</span><span class="pln">err</span><span class="pun">,</span><span class="pln"> warnings</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln"><br /> </span><span class="com">// this callback is called when all is ready...</span><span class="pln"><br /> </span><span class="com">// warnings: if no warnings warnings is null, else it's an array containing errors during require of files</span><span class="pln"><br /></span><span class="pun">});</span><span class="pln"><br /> <br /></span><span class="com">// or</span><span class="pln"><br /> <br />domain</span><span class="pun">.</span><span class="pln">init</span><span class="pun">();</span><span class="pln"> </span><span class="com">// callback is optional</span></code></pre></div></div></div><div class="row-fluid"><div class="span4"><h4 class="feature-title">Handling a command</h4></div><div class="span8"><div class="feature"><pre><code><span class="pln">domain</span><span class="pun">.</span><span class="pln">handle</span><span class="pun">({</span><span class="pln"><br /> id</span><span class="pun">:</span><span class="pln"> </span><span class="str">'b80ade36-dd05-4340-8a8b-846eea6e286f'</span><span class="pun">,</span><span class="pln"><br /> name</span><span class="pun">:</span><span class="pln"> </span><span class="str">'enterNewPerson'</span><span class="pun">,</span><span class="pln"><br /> aggregate</span><span class="pun">:</span><span class="pln"> </span><span class="pun">{</span><span class="pln"><br /> id</span><span class="pun">:</span><span class="pln"> </span><span class="str">'3b4d44b0-34fb-4ceb-b212-68fe7a7c2f70'</span><span class="pun">,</span><span class="pln"><br /> name</span><span class="pun">:</span><span class="pln"> </span><span class="str">'person'</span><span class="pln"><br /> </span><span class="pun">},</span><span class="pln"><br /> context</span><span class="pun">:</span><span class="pln"> </span><span class="pun">{</span><span class="pln"><br /> name</span><span class="pun">:</span><span class="pln"> </span><span class="str">'hr'</span><span class="pln"><br /> </span><span class="pun">},</span><span class="pln"><br /> payload</span><span class="pun">:</span><span class="pln"> </span><span class="pun">{</span><span class="pln"><br /> firstname</span><span class="pun">:</span><span class="pln"> </span><span class="str">'Jack'</span><span class="pun">,</span><span class="pln"><br /> lastname</span><span class="pun">:</span><span class="pln"> </span><span class="str">'Huston'</span><span class="pln"><br /> </span><span class="pun">},</span><span class="pln"><br /> revision</span><span class="pun">:</span><span class="pln"> </span><span class="lit">0</span><span class="pun">,</span><span class="pln"><br /> version</span><span class="pun">:</span><span class="pln"> </span><span class="lit">1</span><span class="pun">,</span><span class="pln"><br /> meta</span><span class="pun">:</span><span class="pln"> </span><span class="pun">{</span><span class="pln"><br /> userId</span><span class="pun">:</span><span class="pln"> </span><span class="str">'ccd65819-4da4-4df9-9f24-5b10bf89ef89'</span><span class="pln"><br /> </span><span class="pun">}</span><span class="pln"><br /></span><span class="pun">});</span><span class="pln"> </span><span class="com">// callback is optional</span></code></pre></div></div></div><div class="row-fluid"><div class="span4"><p>or</p></div><div class="span8"><div class="feature"><pre><code><span class="pln">domain</span><span class="pun">.</span><span class="pln">handle</span><span class="pun">({</span><span class="pln"><br /> id</span><span class="pun">:</span><span class="pln"> </span><span class="str">'b80ade36-dd05-4340-8a8b-846eea6e286f'</span><span class="pun">,</span><span class="pln"><br /> name</span><span class="pun">:</span><span class="pln"> </span><span class="str">'renamePerson'</span><span class="pun">,</span><span class="pln"><br /> aggregate</span><span class="pun">:</span><span class="pln"> </span><span class="pun">{</span><span class="pln"><br /> id</span><span class="pun">:</span><span class="pln"> </span><span class="str">'3b4d44b0-34fb-4ceb-b212-68fe7a7c2f70'</span><span class="pun">,</span><span class="pln"><br /> name</span><span class="pun">:</span><span class="pln"> </span><span class="str">'person'</span><span class="pln"><br /> </span><span class="pun">},</span><span class="pln"><br /> context</span><span class="pun">:</span><span class="pln"> </span><span class="pun">{</span><span class="pln"><br /> name</span><span class="pun">:</span><span class="pln"> </span><span class="str">'hr'</span><span class="pln"><br /> </span><span class="pun">},</span><span class="pln"><br /> payload</span><span class="pun">:</span><span class="pln"> </span><span class="pun">{</span><span class="pln"><br /> firstname</span><span class="pun">:</span><span class="pln"> </span><span class="str">'Jack'</span><span class="pun">,</span><span class="pln"><br /> lastname</span><span class="pun">:</span><span class="pln"> </span><span class="str">'Huston'</span><span class="pln"><br /> </span><span class="pun">},</span><span class="pln"><br /> revision</span><span class="pun">:</span><span class="pln"> </span><span class="lit">0</span><span class="pun">,</span><span class="pln"><br /> version</span><span class="pun">:</span><span class="pln"> </span><span class="lit">1</span><span class="pun">,</span><span class="pln"><br /> meta</span><span class="pun">:</span><span class="pln"> </span><span class="pun">{</span><span class="pln"><br /> userId</span><span class="pun">:</span><span class="pln"> </span><span class="str">'ccd65819-4da4-4df9-9f24-5b10bf89ef89'</span><span class="pln"><br /> </span><span class="pun">}</span><span class="pln"><br /></span><span class="pun">},</span><span class="pln"> </span><span class="kwd">function</span><span class="pln"> </span><span class="pun">(</span><span class="pln">err</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln"><br /> </span><span class="com">// this callback is called when command is handled successfully or unsuccessfully</span><span class="pln"><br /> </span><span class="com">// err can be of type:</span><span class="pln"><br /> </span><span class="com">// - null</span><span class="pln"><br /> </span><span class="com">// - Error</span><span class="pln"><br /> </span><span class="com">// {</span><span class="pln"><br /> </span><span class="com">// name: 'Error',</span><span class="pln"><br /> </span><span class="com">// message: 'optional message'</span><span class="pln"><br /> </span><span class="com">// }</span><span class="pln"><br /> </span><span class="com">// - ValidationError</span><span class="pln"><br /> </span><span class="com">// {</span><span class="pln"><br /> </span><span class="com">// name: 'ValidationError',</span><span class="pln"><br /> </span><span class="com">// message: 'some message',</span><span class="pln"><br /> </span><span class="com">// more: { /* more infos */ }</span><span class="pln"><br /> </span><span class="com">// }</span><span class="pln"><br /> </span><span class="com">// - BusinessRuleError</span><span class="pln"><br /> </span><span class="com">// {</span><span class="pln"><br /> </span><span class="com">// name: 'BusinessRuleError',</span><span class="pln"><br /> </span><span class="com">// message: 'some message',</span><span class="pln"><br /> </span><span class="com">// more: { /* more infos */ }</span><span class="pln"><br /> </span><span class="com">// }</span><span class="pln"><br /> </span><span class="com">// - AggregateDestroyedError</span><span class="pln"><br /> </span><span class="com">// {</span><span class="pln"><br /> </span><span class="com">// name: 'AggregateDestroyedError',</span><span class="pln"><br /> </span><span class="com">// message: 'Aggregate has already been destroyed!',</span><span class="pln"><br /> </span><span class="com">// more: {</span><span class="pln"><br /> </span><span class="com">// aggregateId: 'ad10d2c0-6509-4cb0-86d2-76312d930001',</span><span class="pln"><br /> </span><span class="com">// aggregateRevision: 6</span><span class="pln"><br /> </span><span class="com">// }</span><span class="pln"><br /> </span><span class="com">// }</span><span class="pln"><br /> </span><span class="com">// - AggregateConcurrencyError</span><span class="pln"><br /> </span><span class="com">// {</span><span class="pln"><br /> </span><span class="com">// name: 'AggregateConcurrencyError',</span><span class="pln"><br /> </span><span class="com">// message: 'Actual revision in command is "3", but expected is "2"!',</span><span class="pln"><br /> </span><span class="com">// more: {</span><span class="pln"><br /> </span><span class="com">// aggregateId: 'ad10d2c0-6509-4cb0-86d2-76312d930001',</span><span class="pln"><br /> </span><span class="com">// aggregateRevision: 2,</span><span class="pln"><br /> </span><span class="com">// commandRevision: 3</span><span class="pln"><br /> </span><span class="com">// }</span><span class="pln"><br /> </span><span class="com">// }</span><span class="pln"><br /></span><span class="pun">});</span></code></pre></div></div></div><div class="row-fluid"><div class="span4"><p>more infos, can be useful if testing</p></div><div class="span8"><div class="feature"><pre><code><span class="pln">domain</span><span class="pun">.</span><span class="pln">handle</span><span class="pun">({</span><span class="pln"><br /> id</span><span class="pun">:</span><span class="pln"> </span><span class="str">'b80ade36-dd05-4340-8a8b-846eea6e286f'</span><span class="pun">,</span><span class="pln"><br /> name</span><span class="pun">:</span><span class="pln"> </span><span class="str">'renamePerson'</span><span class="pun">,</span><span class="pln"><br /> aggregate</span><span class="pun">:</span><span class="pln"> </span><span class="pun">{</span><span class="pln"><br /> id</span><span class="pun">:</span><span class="pln"> </span><span class="str">'3b4d44b0-34fb-4ceb-b212-68fe7a7c2f70'</span><span class="pun">,</span><span class="pln"><br /> name</span><span class="pun">:</span><span class="pln"> </span><span class="str">'person'</span><span class="pln"><br /> </span><span class="pun">},</span><span class="pln"><br /> context</span><span class="pun">:</span><span class="pln"> </span><span class="pun">{</span><span class="pln"><br /> name</span><span class="pun">:</span><span class="pln"> </span><span class="str">'hr'</span><span class="pln"><br /> </span><span class="pun">},</span><span class="pln"><br /> payload</span><span class="pun">:</span><span class="pln"> </span><span class="pun">{</span><span class="pln"><br /> firstname</span><span class="pun">:</span><span class="pln"> </span><span class="str">'Jack'</span><span class="pun">,</span><span class="pln"><br /> lastname</span><span class="pun">:</span><span class="pln"> </span><span class="str">'Huston'</span><span class="pln"><br /> </span><span class="pun">},</span><span class="pln"><br /> revision</span><span class="pun">:</span><span class="pln"> </span><span class="lit">0</span><span class="pun">,</span><span class="pln"><br /> version</span><span class="pun">:</span><span class="pln"> </span><span class="lit">1</span><span class="pun">,</span><span class="pln"><br /> meta</span><span class="pun">:</span><span class="pln"> </span><span class="pun">{</span><span class="pln"><br /> userId</span><span class="pun">:</span><span class="pln"> </span><span class="str">'ccd65819-4da4-4df9-9f24-5b10bf89ef89'</span><span class="pln"><br /> </span><span class="pun">}</span><span class="pln"><br /></span><span class="pun">},</span><span class="pln"> </span><span class="kwd">function</span><span class="pln"> </span><span class="pun">(</span><span class="pln">err</span><span class="pun">,</span><span class="pln"> events</span><span class="pun">,</span><span class="pln"> aggregateData</span><span class="pun">,</span><span class="pln"> metaInfos</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln"><br /> </span><span class="com">// this callback is called when command is handled successfully or unsuccessfully</span><span class="pln"><br /> </span><span class="com">// err: is the same as described before</span><span class="pln"><br /> <br /> </span><span class="com">// events: same as passed in 'onEvent' function</span><span class="pln"><br /> </span><span class="com">// events: in case of no error here is the array of all events that should be published</span><span class="pln"><br /> </span><span class="com">// events: in case of error are the one of these Errors (ValidationError, BusinessRuleError, AggregateDestroyedError, AggregateConcurrencyError)</span><span class="pln"><br /> </span><span class="com">// converted in an event with the event name defined in the options (default is 'commandRejected')</span><span class="pln"><br /> <br /> </span><span class="com">// aggregateData: represents the aggregateData after applying the resulting events</span><span class="pln"><br /> <br /> </span><span class="com">// metaInfos: { aggregateId: '3b4d44b0-34fb-4ceb-b212-68fe7a7c2f70', aggregate: 'person', context: 'context' }</span><span class="pln"><br /></span><span class="pun">});</span></code></pre></div></div></div></div></div><div class="row-fluid"><div class="span12"><h2>Request domain information</h2><div class="row-fluid"><div class="span4"><h4 class="feature-title">After the initialization you can request the domain information:</h4></div><div class="span8"><div class="feature"><pre><code><span class="pln">domain</span><span class="pun">.</span><span class="pln">init</span><span class="pun">(</span><span class="kwd">function</span><span class="pln"> </span><span class="pun">(</span><span class="pln">err</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln"><br /> domain</span><span class="pun">.</span><span class="pln">getInfo</span><span class="pun">();</span><span class="pln"><br /> </span><span class="com">// ==></span><span class="pln"><br /> </span><span class="com">// { contexts: [</span><span class="pln"><br /> </span><span class="com">// {</span><span class="pln"><br /> </span><span class="com">// "name": "hr",</span><span class="pln"><br /> </span><span class="com">// "aggregates": [</span><span class="pln"><br /> </span><span class="com">// {</span><span class="pln"><br /> </span><span class="com">// "name": "person",</span><span class="pln"><br /> </span><span class="com">// "version": 3,</span><span class="pln"><br /> </span><span class="com">// "commands": [</span><span class="pln"><br /> </span><span class="com">// {</span><span class="pln"><br /> </span><span class="com">// "name": "enterNewPerson",</span><span class="pln"><br /> </span><span class="com">// "version": 0,</span><span class="pln"><br /> </span><span class="com">// "preconditions": [...]</span><span class="pln"><br /> </span><span class="com">// },</span><span class="pln"><br /> </span><span class="com">// {</span><span class="pln"><br /> </span><span class="com">// "name": "unregisterAllContactInformation",</span><span class="pln"><br /> </span><span class="com">// "version": 2,</span><span class="pln"><br /> </span><span class="com">// "preconditions": [...]</span><span class="pln"><br /> </span><span class="com">// },</span><span class="pln"><br /> </span><span class="com">// {</span><span class="pln"><br /> </span><span class="com">// "name": "unregisterAllContactInformation",</span><span class="pln"><br /> </span><span class="com">// "version": 1,</span><span class="pln"><br /> </span><span class="com">// "preconditions": [...]</span><span class="pln"><br /> </span><span class="com">// }</span><span class="pln"><br /> </span><span class="com">// ],</span><span class="pln"><br /> </span><span class="com">// "events": [</span><span class="pln"><br /> </span><span class="com">// {</span><span class="pln"><br /> </span><span class="com">// "name": "enteredNewPerson",</span><span class="pln"><br /> </span><span class="com">// "version": 3</span><span class="pln"><br /> </span><span class="com">// },</span><span class="pln"><br /> </span><span class="com">// {</span><span class="pln"><br /> </span><span class="com">// "name": "enteredNewPerson",</span><span class="pln"><br /> </span><span class="com">// "version": 0</span><span class="pln"><br /> </span><span class="com">// },</span><span class="pln"><br /> </span><span class="com">// {</span><span class="pln"><br /> </span><span class="com">// "name": "enteredNewPerson",</span><span class="pln"><br /> </span><span class="com">// "version": 2</span><span class="pln"><br /> </span><span class="com">// },</span><span class="pln"><br /> </span><span class="com">// {</span><span class="pln"><br /> </span><span class="com">// "name": "unregisteredEMailAddress",</span><span class="pln"><br /> </span><span class="com">// "version": 0</span><span class="pln"><br /> </span><span class="com">// },</span><span class="pln"><br /> </span><span class="com">// {</span><span class="pln"><br /> </span><span class="com">// "name": "unregisteredPhoneNumber",</span><span class="pln"><br /> </span><span class="com">// "version": 0</span><span class="pln"><br /> </span><span class="com">// }</span><span class="pln"><br /> </span><span class="com">// ]</span><span class="pln"><br /> </span><span class="com">// "businessRules": [</span><span class="pln"><br /> </span><span class="com">// {</span><span class="pln"><br /> </span><span class="com">// "name": "atLeast1EMail",</span><span class="pln"><br /> </span><span class="com">// "description": "at least one character should be in email address"</span><span class="pln"><br /> </span><span class="com">// },</span><span class="pln"><br /> </span><span class="com">// {</span><span class="pln"><br /> </span><span class="com">// "name": "nameEquality",</span><span class="pln"><br /> </span><span class="com">// "description": "firstname should never be equal lastname"</span><span class="pln"><br /> </span><span class="com">// }</span><span class="pln"><br /> </span><span class="com">// ]</span><span class="pln"><br /> </span><span class="com">// }</span><span class="pln"><br /> </span><span class="com">// ]</span><span class="pln"><br /> </span><span class="com">// }</span><span class="pln"><br /> </span><span class="com">//]}</span><span class="pln"><br /></span><span class="pun">});</span></code></pre></div></div></div></div></div><div class="row-fluid"><div class="span12"><h2>Components definition</h2><div class="row-fluid"><div class="span4"><h4 class="feature-title">Context</h4></div><div class="span8"><div class="feature"><pre><code><span class="kwd">module</span><span class="pun">.</span><span class="pln">exports </span><span class="pun">=</span><span class="pln"> </span><span class="kwd">require</span><span class="pun">(</span><span class="str">'cqrs-domain'</span><span class="pun">).</span><span class="pln">defineContext</span><span class="pun">({</span><span class="pln"><br /> </span><span class="com">// optional, default is the directory name</span><span class="pln"><br /> name</span><span class="pun">:</span><span class="pln"> </span><span class="str">'hr'</span><span class="pln"><br /></span><span class="pun">});</span></code></pre></div></div></div><div class="row-fluid"><div class="span4"><h4 class="feature-title">Aggregate</h4></div><div class="span8"><div class="feature"><pre><code><span class="kwd">module</span><span class="pun">.</span><span class="pln">exports </span><span class="pun">=</span><span class="pln"> </span><span class="kwd">require</span><span class="pun">(</span><span class="str">'cqrs-domain'</span><span class="pun">).</span><span class="pln">defineAggregate</span><span class="pun">({</span><span class="pln"><br /> </span><span class="com">// optional, default is last part of path name</span><span class="pln"><br /> name</span><span class="pun">:</span><span class="pln"> </span><span class="str">'person'</span><span class="pun">,</span><span class="pln"><br /> <br /> </span><span class="com">// optional, default 0</span><span class="pln"><br /> version</span><span class="pun">:</span><span class="pln"> </span><span class="lit">3</span><span class="pun">,</span><span class="pln"><br /> <br /> </span><span class="com">// optional, default ''</span><span class="pln"><br /> defaultCommandPayload</span><span class="pun">:</span><span class="pln"> </span><span class="str">'payload'</span><span class="pun">,</span><span class="pln"><br /> <br /> </span><span class="com">// optional, default ''</span><span class="pln"><br /> defaultEventPayload</span><span class="pun">:</span><span class="pln"> </span><span class="str">'payload'</span><span class="pun">,</span><span class="pln"><br /> <br /> </span><span class="com">// optional, default ''</span><span class="pln"><br /> defaultPreConditionPayload</span><span class="pun">:</span><span class="pln"> </span><span class="str">'payload'</span><span class="pun">,</span><span class="pln"><br /> <br /> </span><span class="com">// optional, default false</span><span class="pln"><br /> </span><span class="com">// by skipping the history, only the last event will be loaded and not applyed (just to ensure the revision number increment)</span><span class="pln"><br /> skipHistory</span><span class="pun">:</span><span class="pln"> </span><span class="kwd">true</span><span class="pun">,</span><span class="pln"><br /> <br /> </span><span class="com">// optional, default false</span><span class="pln"><br /> </span><span class="com">// only optionally needed when skipHistory is set to true, only the last event will be loaded and applyed</span><span class="pln"><br /> applyLastEvent</span><span class="pun">:</span><span class="pln"> </span><span class="kwd">true</span><span class="pun">,</span><span class="pln"><br /> <br /> </span><span class="com">// optional, default false</span><span class="pln"><br /> </span><span class="com">// will publish the events but will not commit them to the eventstore</span><span class="pln"><br /> disablePersistence</span><span class="pun">:</span><span class="pln"> </span><span class="kwd">false</span><span class="pln"><br /></span><span class="pun">},</span><span class="pln"><br /> <br /></span><span class="com">// optionally, define some initialization data...</span><span class="pln"><br /></span><span class="pun">{</span><span class="pln"><br /> emails</span><span class="pun">:</span><span class="pln"> </span><span class="pun">[</span><span class="str">'[email protected]'</span><span class="pun">],</span><span class="pln"><br /> phoneNumbers</span><span class="pun">:</span><span class="pln"> </span><span class="pun">[]</span><span class="pln"><br /></span><span class="pun">})</span><span class="pln"><br /> <br /></span><span class="com">// optionally, define snapshot need algorithm...</span><span class="pln"><br /></span><span class="pun">.</span><span class="pln">defineSnapshotNeed</span><span class="pun">(</span><span class="kwd">function</span><span class="pln"> </span><span class="pun">(</span><span class="pln">loadingTime</span><span class="pun">,</span><span class="pln"> events</span><span class="pun">,</span><span class="pln"> aggregateData</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln"><br /> </span><span class="com">// loadingTime is the loading time in ms of the eventstore data</span><span class="pln"><br /> </span><span class="com">// events are all loaded events in an array</span><span class="pln"><br /> </span><span class="com">// aggregateData represents the aggregateData after applying the resulting events</span><span class="pln"><br /> </span><span class="kwd">return</span><span class="pln"> events</span><span class="pun">.</span><span class="pln">length </span><span class="pun">>=</span><span class="pln"> </span><span class="lit">200</span><span class="pun">;</span><span class="pln"><br /></span><span class="pun">})</span><span class="pln"><br /> <br /></span><span class="com">// optionally, define if snapshot should be ignored</span><span class="pln"><br /></span><span class="com">// if true, the whole event stream will be loaded</span><span class="pln"><br /></span><span class="pun">.</span><span class="pln">defineIgnoreSnapshot</span><span class="pun">({</span><span class="pln"><br /> version</span><span class="pun">:</span><span class="pln"> </span><span class="lit">0</span><span class="pln"><br /></span><span class="pun">},</span><span class="pln"> </span><span class="kwd">function</span><span class="pln"> </span><span class="pun">(</span><span class="pln">data</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln"><br /> </span><span class="kwd">return</span><span class="pln"> </span><span class="kwd">true</span><span class="pun">;</span><span class="pln"><br /></span><span class="pun">})</span><span class="pln"><br /></span><span class="com">//.defineIgnoreSnapshot({</span><span class="pln"><br /></span><span class="com">// version: 0</span><span class="pln"><br /></span><span class="com">//}, true)</span><span class="pln"><br /></span><span class="com">//.defineIgnoreSnapshot({</span><span class="pln"><br /></span><span class="com">// version: 0</span><span class="pln"><br /></span><span class="com">//}) // default true</span><span class="pln"><br /> <br /></span><span class="com">// optionally, define conversion algorithm for older snapshots</span><span class="pln"><br /></span><span class="com">// always convert directly to newest version...</span><span class="pln"><br /></span><span class="com">// when loaded a snapshot and it's an older snapshot, a new snapshot with same revision but with newer aggregate version will be created</span><span class="pln"><br /></span><span class="pun">.</span><span class="pln">defineSnapshotConversion</span><span class="pun">({</span><span class="pln"><br /> version</span><span class="pun">:</span><span class="pln"> </span><span class="lit">1</span><span class="pln"><br /></span><span class="pun">},</span><span class="pln"> </span><span class="kwd">function</span><span class="pln"> </span><span class="pun">(</span><span class="pln">data</span><span class="pun">,</span><span class="pln"> aggregate</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln"><br /> </span><span class="com">// data is the snapshot data</span><span class="pln"><br /> </span><span class="com">// aggregate is the aggregate object</span><span class="pln"><br /></span><span class="pun">|</span><span class="pln"><br /> aggregate</span><span class="pun">.</span><span class="kwd">set</span><span class="pun">(</span><span class="str">'emails'</span><span class="pun">,</span><span class="pln"> data</span><span class="pun">.</span><span class="pln">emails</span><span class="pun">);</span><span class="pln"><br /> aggregate</span><span class="pun">.</span><span class="kwd">set</span><span class="pun">(</span><span class="str">'phoneNumbers'</span><span class="pun">,</span><span class="pln"> data</span><span class="pun">.</span><span class="pln">phoneNumbers</span><span class="pun">);</span><span class="pln"><br /></span><span class="pun">|</span><span class="pln"><br /> </span><span class="kwd">var</span><span class="pln"> names </span><span class="pun">=</span><span class="pln"> data</span><span class="pun">.</span><span class="pln">name</span><span class="pun">.</span><span class="pln">split</span><span class="pun">(</span><span class="str">' '</span><span class="pun">);</span><span class="pln"><br /> aggregate</span><span class="pun">.</span><span class="kwd">set</span><span class="pun">(</span><span class="str">'firstname'</span><span class="pun">,</span><span class="pln"> names</span><span class="pun">[</span><span class="lit">0</span><span class="pun">]);</span><span class="pln"><br /> aggregate</span><span class="pun">.</span><span class="kwd">set</span><span class="pun">(</span><span class="str">'lastname'</span><span class="pun">,</span><span class="pln"> names</span><span class="pun">[</span><span class="lit">1</span><span class="pun">]);</span><span class="pln"><br /></span><span class="pun">})</span><span class="pln"><br /></span><span class="com">// optionally, define idGenerator function for new aggregate ids</span><span class="pln"><br /></span><span class="com">// sync</span><span class="pln"><br /></span><span class="pun">.</span><span class="pln">defineAggregateIdGenerator</span><span class="pun">(</span><span class="kwd">function</span><span class="pln"> </span><span class="pun">()</span><span class="pln"> </span><span class="pun">{</span><span class="pln"><br /> </span><span class="kwd">return</span><span class="pln"> </span><span class="kwd">require</span><span class="pun">(</span><span class="str">'node-uuid'</span><span class="pun">).</span><span class="pln">v4</span><span class="pun">().</span><span class="pln">toString</span><span class="pun">();</span><span class="pln"><br /></span><span class="pun">});</span><span class="pln"><br /></span><span class="com">// or async</span><span class="pln"><br /></span><span class="pun">.</span><span class="pln">defineAggregateIdGenerator</span><span class="pun">(</span><span class="kwd">function</span><span class="pln"> </span><span class="pun">(</span><span class="pln">callback</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln"><br /> setTimeout</span><span class="pun">(</span><span class="kwd">function</span><span class="pln"> </span><span class="pun">()</span><span class="pln"> </span><span class="pun">{</span><span class="pln"><br /> </span><span class="kwd">var</span><span class="pln"> id </span><span class="pun">=</span><span class="pln"> </span><span class="kwd">require</span><span class="pun">(</span><span class="str">'node-uuid'</span><span class="pun">).</span><span class="pln">v4</span><span class="pun">().</span><span class="pln">toString</span><span class="pun">();</span><span class="pln"><br /> callback</span><span class="pun">(</span><span class="kwd">null</span><span class="pun">,</span><span class="pln"> id</span><span class="pun">);</span><span class="pln"><br /> </span><span class="pun">},</span><span class="pln"> </span><span class="lit">50</span><span class="pun">);</span><span class="pln"><br /></span><span class="pun">})</span><span class="pln"><br /></span><span class="com">// optionally, define idGenerator function for new aggregate ids that are command aware</span><span class="pln"><br /></span><span class="com">// if you define it that way, the normal defineAggregateIdGenerator function will be replaced</span><span class="pln"><br /></span><span class="com">// sync</span><span class="pln"><br /></span><span class="pun">.</span><span class="pln">defineCommandAwareAggregateIdGenerator</span><span class="pun">(</span><span class="kwd">function</span><span class="pln"> </span><span class="pun">(</span><span class="pln">cmd</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln"><br /> </span><span class="kwd">return</span><span class="pln"> cmd</span><span class="pun">.</span><span class="pln">id </span><span class="pun">+</span><span class="pln"> </span><span class="kwd">require</span><span class="pun">(</span><span class="str">'uuid'</span><span class="pun">).</span><span class="pln">v4</span><span class="pun">().</span><span class="pln">toString</span><span class="pun">();</span><span class="pln"><br /></span><span class="pun">});</span><span class="pln"><br /></span><span class="com">// or async</span><span class="pln"><br /></span><span class="pun">.</span><span class="pln">defineCommandAwareAggregateIdGenerator</span><span class="pun">(</span><span class="kwd">function</span><span class="pln"> </span><span class="pun">(</span><span class="pln">cmd</span><span class="pun">,</span><span class="pln"> callback</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln"><br /> setTimeout</span><span class="pun">(</span><span class="kwd">function</span><span class="pln"> </span><span class="pun">()</span><span class="pln"> </span><span class="pun">{</span><span class="pln"><br /> </span><span class="kwd">var</span><span class="pln"> id </span><span class="pun">=</span><span class="pln"> cmd</span><span class="pun">.</span><span class="pln">id </span><span class="pun">+</span><span class="pln"> </span><span class="kwd">require</span><span class="pun">(</span><span class="str">'uuid'</span><span class="pun">).</span><span class="pln">v4</span><span class="pun">().</span><span class="pln">toString</span><span class="pun">();</span><span class="pln"><br /> callback</span><span class="pun">(</span><span class="kwd">null</span><span class="pun">,</span><span class="pln"> id</span><span class="pun">);</span><span class="pln"><br /> </span><span class="pun">},</span><span class="pln"> </span><span class="lit">50</span><span class="pun">);</span><span class="pln"><br /></span><span class="pun">});</span></code></pre></div></div></div><div class="row-fluid"><div class="span4"><h4 class="feature-title">Command validation</h4></div><div class="span8"><div class="feature"><p>All command schemas are json schemas. Hint<a href="http://jsonary.com/documentation/json-schema/">http://jsonary.com/documentation/json-schema/</a></p><p>Internally the<a href="http://geraintluff.github.io/tv4/">tv4</a> module is used for validation.</p><p>Additionaly you can extend the tv4 instance with other functionality like<a href="https://github.com/ikr/tv4-formats">tv4-formats</a>, so you can easily use format constraints (i.e. 'email') for your 'string'-types.</p><p>To extend tv4 just catch the tv4 instance after having initialized the domain:<pre><code><span class="pln">domain</span><span class="pun">.</span><span class="pln">init</span><span class="pun">(</span><span class="kwd">function</span><span class="pln"> </span><span class="pun">(</span><span class="pln">err</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln"><br /> domain</span><span class="pun">.</span><span class="pln">getTv4</span><span class="pun">().</span><span class="pln">addFormat</span><span class="pun">(</span><span class="kwd">require</span><span class="pun">(</span><span class="str">'tv4-formats'</span><span class="pun">));</span><span class="pln"><br /></span><span class="pun">});</span></code></pre></p><p>Each command schema title should match the command name. Example:<a href="https://github.com/adrai/node-cqrs-domain/blob/1.0/test/integration/fixture/set1/hr/person/validationRules/enterNewPerson.json">enterNewPerson.json</a></p><p>To support multiple versions look at:<a href="https://github.com/adrai/node-cqrs-domain/blob/v2.1.5/test/integration/fixture/set1/hr/person/validationRules/unregisterAllContactInformation.json#L10">unregisterAllContactInformation.json</a>or:<a href="https://github.com/adrai/node-cqrs-domain/blob/v2.2.0/test/integration/fixture/set1/hr/person/validationRules/unregisterAllContactInformation_v1.json#L3">unregisterAllContactInformation_v1.json</a><a href="https://github.com/adrai/node-cqrs-domain/blob/v2.2.0/test/integration/fixture/set1/hr/person/validationRules/unregisterAllContactInformation_v2.json#L3">unregisterAllContactInformation_v2.json</a></p><p>You can also have an hierarchical command extension look at:</p><ul><li><a href="https://github.com/adrai/node-cqrs-domain/blob/1.0/test/integration/fixture/set1/hr/person/validationRules/enterNewPerson.json">command</a></li><li><a href="https://github.com/adrai/node-cqrs-domain/blob/1.0/test/integration/fixture/set1/hr/person/command.json">aggregate</a></li><li><a href="https://github.com/adrai/node-cqrs-domain/blob/1.0/test/integration/fixture/set1/hr/command.json">context</a></li><li><a href="https://github.com/adrai/node-cqrs-domain/blob/1.0/test/integration/fixture/set1/command.json">general</a></li></ul></div></div></div><div class="row-fluid"><div class="span4"><h4 class="feature-title">Pre-Load-Condition</h4><p>Can be used to perform some business rules before handling the command. Contrary to Pre-Conditions, these rules are applied BEFORE the aggregate is loaded.</p><p>This allows you to (for example) run checks against external information by using closures.</p><p>A Command can have multiple pre-load-conditions.</p></div><div class="span8"><div class="feature"><pre><code><span class="kwd">var</span><span class="pln"> externalDataLoader </span><span class="pun">=</span><span class="pln"> </span><span class="kwd">require</span><span class="pun">(</span><span class="str">'some_file'</span><span class="pun">);</span><span class="pln"><br /> <br /></span><span class="kwd">module</span><span class="pun">.</span><span class="pln">exports </span><span class="pun">=</span><span class="pln"> </span><span class="kwd">require</span><span class="pun">(</span><span class="str">'cqrs-domain'</span><span class="pun">).</span><span class="pln">definePreLoadCondition</span><span class="pun">({</span><span class="pln"><br /> </span><span class="com">// the command name</span><span class="pln"><br /> </span><span class="com">// optional, default is file name without extension,</span><span class="pln"><br /> </span><span class="com">// if name is '' it will handle all commands that matches the appropriate aggregate</span><span class="pln"><br /> </span><span class="com">// if name is an array of strings it will handle all commands that matches the appropriate name</span><span class="pln"><br /> name</span><span class="pun">:</span><span class="pln"> </span><span class="str">'checkSomeViewModel'</span><span class="pun">,</span><span class="pln"><br /> <br /> </span><span class="com">// optional, default 0</span><span class="pln"><br /> version</span><span class="pun">:</span><span class="pln"> </span><span class="lit">2</span><span class="pun">,</span><span class="pln"><br /> <br /> </span><span class="com">// optional, if not defined it will use what is defined as default in aggregate or pass the whole command</span><span class="pln"><br /> payload</span><span class="pun">:</span><span class="pln"> </span><span class="str">'payload'</span><span class="pun">,</span><span class="pln"><br /> <br /> </span><span class="com">// optional</span><span class="pln"><br /> description</span><span class="pun">:</span><span class="pln"> </span><span class="str">'firstname should always be set'</span><span class="pun">,</span><span class="pln"><br /> <br /> </span><span class="com">// optional, default Infinity, all pre-conditions will be sorted by this value</span><span class="pln"><br /> priority</span><span class="pun">:</span><span class="pln"> </span><span class="lit">1</span><span class="pln"><br /></span><span class="pun">},</span><span class="pln"> </span><span class="kwd">function</span><span class="pln"> </span><span class="pun">(</span><span class="pln">data</span><span class="pun">,</span><span class="pln"> callback</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln"><br /> </span><span class="com">// data is the command data</span><span class="pln"><br /> </span><span class="com">// callback is optional, if not defined as function argument you can throw errors or return errors here (sync way)</span><span class="pln"><br /> <br /> </span><span class="kwd">if</span><span class="pln"> </span><span class="pun">(</span><span class="pln">externalDataLoader</span><span class="pun">.</span><span class="kwd">get</span><span class="pun">(</span><span class="pln">data</span><span class="pun">.</span><span class="pln">id</span><span class="pun">)</span><span class="pln"> </span><span class="pun">!==</span><span class="pln"> data</span><span class="pun">.</span><span class="pln">value</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln"><br /> </span><span class="kwd">return</span><span class="pln"> callback</span><span class="pun">(</span><span class="str">'not allowed'</span><span class="pun">);</span><span class="pln"><br /> </span><span class="com">// or</span><span class="pln"><br /> </span><span class="com">// return callback(new Error('not allowed'));</span><span class="pln"><br /> </span><span class="com">// or</span><span class="pln"><br /> </span><span class="com">// return callback(new Error()); // if no error message is defined then the description will be taken</span><span class="pln"><br /> </span><span class="com">// or</span><span class="pln"><br /> </span><span class="com">// return callback(new require('cqrs-domain').BusinessRuleError('not allowed', { /* more infos */ }));</span><span class="pln"><br /> </span><span class="pun">}</span><span class="pln"><br /> <br /> callback</span><span class="pun">(</span><span class="kwd">null</span><span class="pun">);</span><span class="pln"><br /> <br /> </span><span class="com">// or if callback is not defined as function argument</span><span class="pln"><br /> </span><span class="com">// if (externalDataLoader.get(data.id) !== data.value)</span><span class="pln"><br /> </span><span class="com">// return 'not allowed';</span><span class="pln"><br /> </span><span class="com">// // or</span><span class="pln"><br /> </span><span class="com">// // return new Error('not allowed');</span><span class="pln"><br /> </span><span class="com">// // or</span><span class="pln"><br /> </span><span class="com">// // return new Error(); // if no error message is defined then the description will be taken</span><span class="pln"><br /> </span><span class="com">// // or</span><span class="pln"><br /> </span><span class="com">// // throw new Error(); // if no error message is defined then the description will be taken</span><span class="pln"><br /> </span><span class="com">// // or</span><span class="pln"><br /> </span><span class="com">// // throw new Error('not allowed');</span><span class="pln"><br /> </span><span class="com">// // or</span><span class="pln"><br /> </span><span class="com">// // throw new require('cqrs-domain').BusinessRuleError('not allowed', { /* more infos */ });</span><span class="pln"><br /> </span><span class="com">// }</span><span class="pln"><br /></span><span class="pun">});</span></code></pre><div class="alert alert-info feature-description"><h6>Important hint:</h6><p>Pre-load conditions are especially useful when you have checks that you want to run on an aggregate, but when it is OK for those checks to run on potentially stale data (eg a view model). By doing these checks before the aggregate is locked, you avoid creating a locking bottleneck at the aggregate level, and can keep your aggregate smaller because the information for those checks is externalized to the domain. This helps for performance if the domain you are in is performance critical, and helps you keep focus on the real, strongly consistent invariants in your domain.</p></div></div></div></div><div class="row-fluid"><div class="span4"><h4 class="feature-title">Pre-Condition</h4><p>Can be used to perform some business rules before handling the command. The aggregate is locked and loaded before the pre-condition is applied.</p><p>A Command can have multiple pre-conditions.</p></div><div class="span8"><div class="feature"><pre><code><span class="kwd">module</span><span class="pun">.</span><span class="pln">exports </span><span class="pun">=</span><span class="pln"> </span><span class="kwd">require</span><span class="pun">(</span><span class="str">'cqrs-domain'</span><span class="pun">).</span><span class="pln">definePreCondition</span><span class="pun">({</span><span class="pln"><br /> </span><span class="com">// the command name</span><span class="pln"><br /> </span><span class="com">// optional, default is file name without extension</span><span class="pln"><br /> </span><span class="com">// if name is '' it will handle all commands that matches the appropriate aggregate</span><span class="pln"><br /> </span><span class="com">// if name is an array of strings it will handle all commands that matches the appropriate name</span><span class="pln"><br /> name</span><span class="pun">:</span><span class="pln"> </span><span class="str">'unregisterAllContactInformation'</span><span class="pun">,</span><span class="pln"><br /> <br /> </span><span class="com">// optional, default 0</span><span class="pln"><br /> version</span><span class="pun">:</span><span class="pln"> </span><span class="lit">2</span><span class="pun">,</span><span class="pln"><br /> <br /> </span><span class="com">// optional, if not defined it will use what is defined as default in aggregate or pass the whole command</span><span class="pln"><br /> payload</span><span class="pun">:</span><span class="pln"> </span><span class="str">'payload'</span><span class="pun">,</span><span class="pln"><br /> <br /> </span><span class="com">// optional</span><span class="pln"><br /> description</span><span class="pun">:</span><span class="pln"> </span><span class="str">'firstname should always be set'</span><span class="pun">,</span><span class="pln"><br /> <br /> </span><span class="com">// optional, default Infinity, all pre-conditions will be sorted by this value</span><span class="pln"><br /> priority</span><span class="pun">:</span><span class="pln"> </span><span class="lit">1</span><span class="pln"><br /></span><span class="pun">},</span><span class="pln"> </span><span class="kwd">function</span><span class="pln"> </span><span class="pun">(</span><span class="pln">data</span><span class="pun">,</span><span class="pln"> aggregate</span><span class="pun">,</span><span class="pln"> callback</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln"><br /> </span><span class="com">// data is the command data</span><span class="pln"><br /> </span><span class="com">// aggregate is the aggregate object</span><span class="pln"><br /> </span><span class="com">// callback is optional, if not defined as function argument you can throw errors or return errors here (sync way)</span><span class="pln"><br /> <br /> </span><span class="kwd">if</span><span class="pln"> </span><span class="pun">(!</span><span class="pln">agg</span><span class="pun">.</span><span class="pln">has</span><span class="pun">(</span><span class="str">'firstname'</span><span class="pun">))</span><span class="pln"> </span><span class="pun">{</span><span class="pln"><br /> </span><span class="kwd">return</span><span class="pln"> callback</span><span class="pun">(</span><span class="str">'not personalized'</span><span class="pun">);</span><span class="pln"><br /> </span><span class="com">// or</span><span class="pln"><br /> </span><span class="com">// return callback(new Error('not personalized'));</span><span class="pln"><br /> </span><span class="com">// or</span><span class="pln"><br /> </span><span class="com">// return callback(new Error()); // if no error message is defined then the description will be taken</span><span class="pln"><br /> </span><span class="com">// or</span><span class="pln"><br /> </span><span class="com">// return callback(new require('cqrs-domain').BusinessRuleError('not personalized', { /* more infos */ }));</span><span class="pln"><br /> </span><span class="pun">}</span><span class="pln"><br /> callback</span><span class="pun">(</span><span class="kwd">null</span><span class="pun">);</span><span class="pln"><br /> <br /> </span><span class="com">// or if callback is not defined as function argument</span><span class="pln"><br /> </span><span class="com">// if (!agg.has('firstname')) {</span><span class="pln"><br /> </span><span class="com">// return 'not personalized';</span><span class="pln"><br /> </span><span class="com">// // or</span><span class="pln"><br /> </span><span class="com">// // return new Error('not personalized');</span><span class="pln"><br /> </span><span class="com">// // or</span><span class="pln"><br /> </span><span class="com">// // return new Error(); // if no error message is defined then the description will be taken</span><span class="pln"><br /> </span><span class="com">// // or</span><span class="pln"><br /> </span><span class="com">// // throw new Error(); // if no error message is defined then the description will be taken</span><span class="pln"><br /> </span><span class="com">// // or</span><span class="pln"><br /> </span><span class="com">// // throw new Error('not personalized');</span><span class="pln"><br /> </span><span class="com">// // or</span><span class="pln"><br /> </span><span class="com">// // throw new require('cqrs-domain').BusinessRuleError('not personalized', { /* more infos */ });</span><span class="pln"><br /> </span><span class="com">// }</span><span class="pln"><br /></span><span class="pun">});</span></code></pre></div></div></div><div class="row-fluid"><div class="span4"><h4 class="feature-title">Command</h4><p>Collect all needed infos from aggregate to generate your event(s).</p><p>Move checks out of here, the correct places are "business rules", "pre-conditions" or "pre-load consitions"!</p><p>Do NOT manipulate the aggregate here!</p></div><div class="span8"><div class="feature"><pre><code><span class="kwd">module</span><span class="pun">.</span><span class="pln">exports </span><span class="pun">=</span><span class="pln"> </span><span class="kwd">require</span><span class="pun">(</span><span class="str">'cqrs-domain'</span><span class="pun">).</span><span class="pln">defineCommand</span><span class="pun">({</span><span class="pln"><br /> </span><span class="com">// optional, default is file name without extension</span><span class="pln"><br /> name</span><span class="pun">:</span><span class="pln"> </span><span class="str">'enterNewPerson'</span><span class="pun">,</span><span class="pln"><br /> <br /> </span><span class="com">// optional, default 0</span><span class="pln"><br /> version</span><span class="pun">:</span><span class="pln"> </span><span class="lit">1</span><span class="pun">,</span><span class="pln"><br /> <br /> </span><span class="com">// optional, if not defined it will use what is defined as default in aggregate or pass the whole command</span><span class="pln"><br /> payload</span><span class="pun">:</span><span class="pln"> </span><span class="str">'payload'</span><span class="pun">,</span><span class="pln"><br /> <br /> </span><span class="com">// optional, default undefined</span><span class="pln"><br /> </span><span class="com">// if true, ensures the aggregate to exists already before this command was handled</span><span class="pln"><br /> </span><span class="com">// if false, ensures the aggregate to not exists already before this command was handledthis command was handled</span><span class="pln"><br /> existing</span><span class="pun">:</span><span class="pln"> </span><span class="kwd">true</span><span class="pln"><br /></span><span class="pun">},</span><span class="pln"> </span><span class="kwd">function</span><span class="pln"> </span><span class="pun">(</span><span class="pln">data</span><span class="pun">,</span><span class="pln"> aggregate</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln"><br /> </span><span class="com">// data is the command data</span><span class="pln"><br /> </span><span class="com">// aggregate is the aggregate object</span><span class="pln"><br /></span><span class="pun">|</span><span class="pln"><br /> </span><span class="com">// if (aggregate.get('someAttr') === 'someValue' && aggregate.has('special')) { ... }</span><span class="pln"><br /></span><span class="pun">|</span><span class="pln"><br /> aggregate</span><span class="pun">.</span><span class="pln">apply</span><span class="pun">(</span><span class="str">'enteredNewPerson'</span><span class="pun">,</span><span class="pln"> data</span><span class="pun">);</span><span class="pln"><br /> </span><span class="com">// or</span><span class="pln"><br /> </span><span class="com">// aggregate.apply('enteredNewPerson', data, version);</span><span class="pln"><br /> </span><span class="com">// or</span><span class="pln"><br /> </span><span class="com">// aggregate.apply({</span><span class="pln"><br /> </span><span class="com">// event: 'enteredNewPerson',</span><span class="pln"><br /> </span><span class="com">// payload: data</span><span class="pln"><br /> </span><span class="com">// });</span><span class="pln"><br /></span><span class="pun">});</span></code></pre></div></div></div><div class="row-fluid"><div class="span4"><h4 class="feature-title">Event</h4><p>This is the place where you should manipulate your aggregate.</p></div><div class="span8"><div class="feature"><pre><code><span class="kwd">module</span><span class="pun">.</span><span class="pln">exports </span><span class="pun">=</span><span class="pln"> </span><span class="kwd">require</span><span class="pun">(</span><span class="str">'cqrs-domain'</span><span class="pun">).</span><span class="pln">defineEvent</span><span class="pun">({</span><span class="pln"><br /> </span><span class="com">// optional, default is file name without extension</span><span class="pln"><br /> name</span><span class="pun">:</span><span class="pln"> </span><span class="str">'enteredNewPerson'</span><span class="pun">,</span><span class="pln"><br /> <br /> </span><span class="com">// optional, default 0</span><span class="pln"><br /> version</span><span class="pun">:</span><span class="pln"> </span><span class="lit">3</span><span class="pun">,</span><span class="pln"><br /> <br /> </span><span class="com">// optional, if not defined it will use what is defined as default in aggregate or pass the whole event...</span><span class="pln"><br /> payload</span><span class="pun">:</span><span class="pln"> </span><span class="str">'payload'</span><span class="pln"><br /></span><span class="pun">},</span><span class="pln"><br /></span><span class="com">// passing a function is optional</span><span class="pln"><br /></span><span class="kwd">function</span><span class="pln"> </span><span class="pun">(</span><span class="pln">data</span><span class="pun">,</span><span class="pln"> aggregate</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln"><br /> </span><span class="com">// data is the event data</span><span class="pln"><br /> </span><span class="com">// aggregate is the aggregate object</span><span class="pln"><br /></span><span class="pun">|</span><span class="pln"><br /> aggregate</span><span class="pun">.</span><span class="kwd">set</span><span class="pun">(</span><span class="str">'firstname'</span><span class="pun">,</span><span class="pln"> data</span><span class="pun">.</span><span class="pln">firstname</span><span class="pun">);</span><span class="pln"><br /> aggregate</span><span class="pun">.</span><span class="kwd">set</span><span class="pun">(</span><span class="str">'lastname'</span><span class="pun">,</span><span class="pln"> data</span><span class="pun">.</span><span class="pln">lastname</span><span class="pun">);</span><span class="pln"><br /> </span><span class="com">// or</span><span class="pln"><br /> </span><span class="com">// aggregate.set(data);</span><span class="pln"><br /></span><span class="pun">});</span></code></pre></div></div></div><div class="row-fluid"><div class="span4"><h4 class="feature-title">Business Rule</h4></div><div class="span8"><div class="feature"><pre><code><span class="kwd">module</span><span class="pun">.</span><span class="pln">exports </span><span class="pun">=</span><span class="pln"> </span><span class="kwd">require</span><span class="pun">(</span><span class="str">'cqrs-domain'</span><span class="pun">).</span><span class="pln">defineBusinessRule</span><span class="pun">({</span><span class="pln"><br /> </span><span class="com">// optional, default is file name without extension</span><span class="pln"><br /> name</span><span class="pun">:</span><span class="pln"> </span><span class="str">'nameEquality'</span><span class="pun">,</span><span class="pln"><br /> <br /> </span><span class="com">// optional</span><span class="pln"><br /> description</span><span class="pun">:</span><span class="pln"> </span><span class="str">'firstname should never be equal lastname'</span><span class="pun">,</span><span class="pln"><br /> <br /> </span><span class="com">// optional, default Infinity, all business rules will be sorted by this value</span><span class="pln"><br /> priority</span><span class="pun">:</span><span class="pln"> </span><span class="lit">1</span><span class="pln"><br /></span><span class="pun">},</span><span class="pln"> </span><span class="kwd">function</span><span class="pln"> </span><span class="pun">(</span><span class="pln">changed</span><span class="pun">,</span><span class="pln"> previous</span><span class="pun">,</span><span class="pln"> events</span><span class="pun">,</span><span class="pln"> command</span><span class="pun">,</span><span class="pln"> callback</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln"><br /> </span><span class="com">// changed is the new aggregate object</span><span class="pln"><br /> </span><span class="com">// previous is the old aggregate object</span><span class="pln"><br /> </span><span class="com">// events is the array with the resulting events</span><span class="pln"><br /> </span><span class="com">// command the handling command</span><span class="pln"><br /> </span><span class="com">// callback is optional, if not defined as function argument you can throw errors or return errors here (sync way)</span><span class="pln"><br /> <br /> </span><span class="kwd">if</span><span class="pln"> </span><span class="pun">(</span><span class="pln">changed</span><span class="pun">.</span><span class="kwd">get</span><span class="pun">(</span><span class="str">'firstname'</span><span class="pun">)</span><span class="pln"> </span><span class="pun">===</span><span class="pln"> changed</span><span class="pun">.</span><span class="kwd">get</span><span class="pun">(</span><span class="str">'lastname'</span><span class="pun">))</span><span class="pln"> </span><span class="pun">{</span><span class="pln"><br /> </span><span class="kwd">return</span><span class="pln"> callback</span><span class="pun">(</span><span class="str">'names not valid'</span><span class="pun">);</span><span class="pln"><br /> </span><span class="com">// or</span><span class="pln"><br /> </span><span class="com">// return callback(new Error('names not valid'));</span><span class="pln"><br /> </span><span class="com">// or</span><span class="pln"><br /> </span><span class="com">// return callback(new Error()); // if no error message is defined then the description will be taken</span><span class="pln"><br /> </span><span class="com">// or</span><span class="pln"><br /> </span><span class="com">// return callback(new require('cqrs-domain').BusinessRuleError('names not valid', { /* more infos */ }));</span><span class="pln"><br /> </span><span class="pun">}</span><span class="pln"><br /> callback</span><span class="pun">(</span><span class="kwd">null</span><span class="pun">);</span><span class="pln"><br /> <br /> </span><span class="com">// or if callback is not defined as function argument</span><span class="pln"><br /> </span><span class="com">// if (changed.get('firstname') === changed.get('lastname')) {</span><span class="pln"><br /> </span><span class="com">// return 'names not valid';</span><span class="pln"><br /> </span><span class="com">// // or</span><span class="pln"><br /> </span><span class="com">// // return new Error('names not valid');</span><span class="pln"><br /> </span><span class="com">// // or</span><span class="pln"><br /> </span><span class="com">// // return new Error(); // if no error message is defined then the description will be taken</span><span class="pln"><br /> </span><span class="com">// // or</span><span class="pln"><br /> </span><span class="com">// // throw new Error(); // if no error message is defined then the description will be taken</span><span class="pln"><br /> </span><span class="com">// // or</span><span class="pln"><br /> </span><span class="com">// // throw new Error('names not valid');</span><span class="pln"><br /> </span><span class="com">// // or</span><span class="pln"><br /> </span><span class="com">// // throw new require('cqrs-domain').BusinessRuleError('names not valid', { /* more infos */ });</span><span class="pln"><br /> </span><span class="com">// }</span><span class="pln"><br /></span><span class="pun">});</span></code></pre></div></div></div><div class="row-fluid"><div class="span4"><h4 class="feature-title">Command Handler (Be careful!!!)</h4><p>Is your use case not solvable without a custom command handling? Sagas? Micro-Services?</p></div><div class="span8"><div class="feature"><pre><code><span class="kwd">module</span><span class="pun">.</span><span class="pln">exports </span><span class="pun">=</span><span class="pln"> </span><span class="kwd">require</span><span class="pun">(</span><span class="str">'cqrs-domain'</span><span class="pun">).</span><span class="pln">defineCommandHandler</span><span class="pun">({</span><span class="pln"><br /> </span><span class="com">// optional, default is file name without extension</span><span class="pln"><br /> name</span><span class="pun">:</span><span class="pln"> </span><span class="str">'enterNewSpecialPerson'</span><span class="pun">,</span><span class="pln"><br /> <br /> </span><span class="com">// optional, default 0</span><span class="pln"><br /> version</span><span class="pun">:</span><span class="pln"> </span><span class="lit">1</span><span class="pun">,</span><span class="pln"><br /> <br /> </span><span class="com">// optional, if not defined it will pass the whole command...</span><span class="pln"><br /> payload</span><span class="pun">:</span><span class="pln"> </span><span class="str">'payload'</span><span class="pln"><br /></span><span class="pun">},</span><span class="pln"> </span><span class="kwd">function</span><span class="pln"> </span><span class="pun">(</span><span class="pln">aggId</span><span class="pun">,</span><span class="pln"> cmd</span><span class="pun">,</span><span class="pln"> commandHandler</span><span class="pun">,</span><span class="pln"> callback</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln"><br /> </span><span class="com">// aggId is the aggregate id</span><span class="pln"><br /> </span><span class="com">// cmd is the command data</span><span class="pln"><br /> <br /> commandHandler</span><span class="pun">.</span><span class="pln">loadAggregate</span><span class="pun">(</span><span class="pln">cmd</span><span class="pun">.</span><span class="pln">aggregate</span><span class="pun">.</span><span class="pln">id</span><span class="pun">,</span><span class="pln"> </span><span class="kwd">function</span><span class="pln"> </span><span class="pun">(</span><span class="pln">err</span><span class="pun">,</span><span class="pln"> aggregate</span><span class="pun">,</span><span class="pln"> stream</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln"><br /> </span><span class="kwd">if</span><span class="pln"> </span><span class="pun">(</span><span class="pln">err</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln"><br /> </span><span class="kwd">return</span><span class="pln"> callback</span><span class="pun">(</span><span class="pln">err</span><span class="pun">);</span><span class="pln"><br /> </span><span class="pun">}</span><span class="pln"><br /> <br /> callback</span><span class="pun">(</span><span class="kwd">null</span><span class="pun">,</span><span class="pln"> </span><span class="pun">[{</span><span class="pln"> </span><span class="kwd">my</span><span class="pun">:</span><span class="pln"> </span><span class="str">'special'</span><span class="pun">,</span><span class="pln"> ev</span><span class="pun">:</span><span class="pln"> </span><span class="str">'ent'</span><span class="pln"> </span><span class="pun">}]);</span><span class="pln"><br /> <br /></span><span class="com">// // check if destroyed, check revision, validate command</span><span class="pln"><br /></span><span class="com">// var err = commandHandler.verifyAggregate(aggregate, cmd);</span><span class="pln"><br /></span><span class="com">// if (err) {</span><span class="pln"><br /></span><span class="com">// return callback(err);</span><span class="pln"><br /></span><span class="com">// }</span><span class="pln"><br /></span><span class="com">//</span><span class="pln"><br /></span><span class="com">// // call api or emit a command or whatever...</span><span class="pln"><br /></span><span class="com">// // and at the end perhaps you call: commandHandler.handle(cmd, callback);</span><span class="pln"><br /> </span><span class="pun">});</span><span class="pln"><br /></span><span class="pun">});</span></code></pre><div class="alert alert-info feature-description"><h6>Important hint:</h6><p>Is your use case not solvable without a custom command handling? Sagas? Micro-Services?</p></div></div></div></div></div></div></div></div></div></div></div><div class="extra"><div class="extra-inner"><div class="container"><div class="row"><div class="span4"><h4>about me</h4><ul><li><a href="https://github.com/adrai"><i class="icon-github"></i> github</a></li><li><a href="https://twitter.com/adrirai"><i class="icon-twitter"></i> twitter</a></li></ul></div><div class="span4"><h4>help</h4><ul><li><a href="https://github.com/adrai/cqrs/issues">
issues (<i class="icon-github"></i>)</a></li></ul></div><div class="span4"><h4>legal</h4><ul><li><a href="https://github.com/adrai/node-cqrs-domain/blob/master/licence">license</a></li></ul></div></div></div></div></div><footer class="footer"><div class="footer-inner"><div class="container"><div class="row"><div class="span12">the cqrs modules and libraries are freely distributable under the terms of the MIT license.</div></div></div></div></footer></body></html>