Skip to content

Commit

Permalink
Initial commit
Browse files Browse the repository at this point in the history
  • Loading branch information
samhatoum committed May 13, 2020
0 parents commit a872f35
Show file tree
Hide file tree
Showing 31 changed files with 13,331 additions and 0 deletions.
34 changes: 34 additions & 0 deletions .eslintrc
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
{
"parser": "@typescript-eslint/parser",
"parserOptions": {
"project": "./tsconfig.json"
},
"extends": [
"airbnb-typescript",
"airbnb/hooks",
"eslint:recommended",
"plugin:@typescript-eslint/eslint-recommended",
"plugin:@typescript-eslint/recommended",
"plugin:prettier/recommended"
],
"plugins": ["@typescript-eslint", "prettier"],
"rules": {
"import/no-extraneous-dependencies": ["error", {"devDependencies": ["**/*.spec.tsx", "**/*.spec.ts", "**/features/**/*.ts"]}],
"prettier/prettier": "error",
"react/jsx-filename-extension": 0,
"import/prefer-default-export": 0,
"@typescript-eslint/member-delimiter-style": 0,
"@typescript-eslint/semi": 0,
"@typescript-eslint/space-before-function-paren": 0,
"no-underscore-dangle": 0,
"@typescript-eslint/interface-name-prefix": 0,
"@typescript-eslint/no-empty-interface": 0,
"no-cond-assign": ["error", "except-parens"],
"max-classes-per-file": 0,
"lines-between-class-members": 0
},
"env": {
"browser": true,
"jest": true
}
}
23 changes: 23 additions & 0 deletions .github/workflows/workflow.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
name: CI
on: push
jobs:
test:
runs-on: ubuntu-16.04
steps:
- uses: actions/checkout@v2
- uses: bahmutov/npm-install@v1
- run: npm run type-check
- run: npm run test
- uses: paambaati/codeclimate-action@v2.6.0
env:
CC_TEST_REPORTER_ID: 8363f6771695c1066fba12480279030cf48383ac795fd50fd153178f0af45a98
with:
coverageLocations: |
${{github.workspace}}/coverage/jest/lcov.info:lcov
${{github.workspace}}/coverage/cucumber/lcov.info:lcov
debug: true
- name: Release
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
# NPM_TOKEN: ${{ secrets.NPM_TOKEN }}
# run: npx semantic-release
8 changes: 8 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
.idea
node_modules
coverage
lib/
dist/
.cache
.nyc_output
yarn-error.log
5 changes: 5 additions & 0 deletions .npmignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
node_modules
coverage
dist/
.cache
.github
3 changes: 3 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
# Event Sourcing & CQRS with Typescript


8 changes: 8 additions & 0 deletions babel.config.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
module.exports = {
presets: [
['@babel/preset-env', {targets: {node: 'current'}}],
'@babel/preset-react',
'@babel/typescript',
],
plugins: [['@babel/plugin-proposal-class-properties', {loose: true}]],
}
44 changes: 44 additions & 0 deletions manual-releases.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
# manual-releases

This project has an automated release set up. So things are only released when
there are useful changes in the code that justify a release. But sometimes
things get messed up one way or another and we need to trigger the release
ourselves. When this happens, simply bump the number below and commit that with
the following commit message based on your needs:

**Major**

```
fix(release): manually release a major version
There was an issue with a major release, so this manual-releases.md
change is to release a new major version.
Reference: #<the number of a relevant pull request, issue, or commit>
BREAKING CHANGE: <mention any relevant breaking changes (this is what triggers the major version change so don't skip this!)>
```

**Minor**

```
feat(release): manually release a minor version
There was an issue with a minor release, so this manual-releases.md
change is to release a new minor version.
Reference: #<the number of a relevant pull request, issue, or commit>
```

**Patch**

```
fix(release): manually release a patch version
There was an issue with a patch release, so this manual-releases.md
change is to release a new patch version.
Reference: #<the number of a relevant pull request, issue, or commit>
```

The number of times we've had to do a manual release is: 3
126 changes: 126 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,126 @@
{
"name": "event-sourcing-cqrs-with-typescript",
"author": "Sam Hatoum",
"contributors": [
"Joshua Ohlman"
],
"version": "0.0.0-development",
"description": "",
"main": "lib/index.js",
"directories": {
"lib": "./lib"
},
"scripts": {
"test:unit:watch": "jest --watch",
"test:unit": "jest --coverage",
"test": "rm -rf coverage && npm run test:unit",
"prepublish": "rm -rf lib && npm run build",
"build:watch": "npm run build:js -- --watch",
"coverage": "echo Coverage already collected using test runs. Github actions code-climate plugin needs this task :/",
"type-check": "tsc --noEmit",
"type-check:watch": "npm run type-check -- --watch",
"build": "npm run build:types && npm run build:js",
"build:types": "tsc -p tsconfig-build.json",
"build:js": "babel src --out-dir lib --ignore 'src/**/*.spec.ts','src/**/*.spec.tsx' --extensions \".ts,.tsx\" --source-maps inline ",
"start:example": "parcel ./example/public/index.html"
},
"repository": {
"type": "git",
"url": "git+https://github.com/xolvio/xspecs-ng.git"
},
"files": [
"**/lib/*"
],
"license": "MIT",
"dependencies": {
"uuid": "^8.0.0"
},
"devDependencies": {
"@babel/cli": "^7.2.3",
"@babel/core": "^7.2.2",
"@babel/node": "^7.2.2",
"@babel/plugin-proposal-class-properties": "^7.8.3",
"@babel/plugin-syntax-decorators": "^7.8.3",
"@babel/preset-env": "^7.2.3",
"@babel/preset-react": "^7.0.0",
"@babel/preset-typescript": "^7.9.0",
"@testing-library/dom": "^7.2.1",
"@testing-library/jest-dom": "^5.1.0",
"@testing-library/react": "^9.4.0",
"@testing-library/user-event": "^7.1.2",
"@types/jest": "^25.1.1",
"@types/node": "~12.12.23",
"@types/parcel-env": "^0.0.0",
"@typescript-eslint/eslint-plugin": "^2.29.0",
"@typescript-eslint/parser": "^2.29.0",
"babel-jest": "^24.9.0",
"browserslist": "^4.12.0",
"cucumber": "^6.0.5",
"cz-conventional-changelog": "3.1.0",
"eslint": "^6.8.0",
"eslint-config-airbnb": "^18.1.0",
"eslint-config-airbnb-base": "^14.1.0",
"eslint-config-airbnb-typescript": "^7.2.0",
"eslint-config-prettier": "^6.10.1",
"eslint-plugin-import": "^2.20.2",
"eslint-plugin-jsx-a11y": "^6.2.3",
"eslint-plugin-prettier": "^3.1.3",
"eslint-plugin-react": "^7.19.0",
"eslint-plugin-react-hooks": "^2.5.0",
"expect": "^26.0.1",
"husky": "^4.2.1",
"jest": "^25.1.0",
"lint-staged": "^10.0.7",
"nyc": "^15.0.1",
"parcel-bundler": "^1.12.4",
"prettier": "^2.0.4",
"react": "^16.13.1",
"react-dom": "^16.13.1",
"regenerator-runtime": "^0.13.5",
"semantic-release": "^17.0.7",
"sort-package-json": "^1.42.1",
"testdouble": "^3.12.5",
"ts-node": "^8.8.2",
"typescript": "^3.8.3",
"watch": "^1.0.2"
},
"peerDependencies": {
"react": ">= 16.8.0"
},
"keywords": [
"react-hooks",
"domain-driven-design",
"clean-architecture",
"cqrs"
],
"husky": {
"hooks": {
"prepare-commit-msg": "exec < /dev/tty && git cz --hook || true",
"pre-commit": "yarn type-check && yarn test:example:domain && lint-staged"
}
},
"lint-staged": {
"src/*.ts*": [
"eslint --fix",
"jest --findRelatedTests"
],
"example/**/*.ts*": [
"eslint --fix",
"jest --findRelatedTests"
]
},
"jest": {
"testPathIgnorePatterns": [
"<rootDir>/lib"
],
"coverageDirectory": "<rootDir>/coverage/jest"
},
"browserslist": [
"defaults"
],
"config": {
"commitizen": {
"path": "./node_modules/cz-conventional-changelog"
}
}
}
5 changes: 5 additions & 0 deletions prettier.config.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
module.exports = {
semi: false,
singleQuote: true,
bracketSpacing: false,
}
28 changes: 28 additions & 0 deletions src/domain/branch/Branch.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
import {GivenWhenThen} from '../../es-cqrs/GivenWhenThen'
import {guid} from '../../es-cqrs/Guid'
import {Branch} from './Branch'
import {RenameBranch} from './BranchCommands'
import {BranchCreated, BranchRenamed} from './BranchEvents'
import {MissingParameterError} from '../../es-cqrs/Errors'

const GWT = GivenWhenThen(Branch, __dirname)

test('Rename branch', () =>
GWT((Given, When, Then) => {
const branchId = guid()

Given(new BranchCreated(branchId, 'foo'))
When(new RenameBranch(branchId, 'bar', 1))
Then(new BranchRenamed(branchId, 'bar'))
}))

test('Rename branch fail', () =>
GWT((Given, When, Then) => {
const branchId = guid()

Given(new BranchCreated(branchId, 'foo'))
When(new RenameBranch(branchId, '', 1))
expect((): void => {
Then(new BranchRenamed(branchId, 'bar'))
}).toThrow(MissingParameterError)
}))
27 changes: 27 additions & 0 deletions src/domain/branch/Branch.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
import {AggregateRoot} from '../../es-cqrs/AggregateRoot'
import {BranchCreated, BranchRenamed} from './BranchEvents'
import {MissingParameterError} from '../../es-cqrs/Errors'

export class Branch extends AggregateRoot {
private name: string

constructor(guid: string, name: string) {
super()
this.applyChange(new BranchCreated(guid, name))
}

applyBranchCreated(event: BranchCreated): void {
this._id = event.aggregateId
this.name = event.name
}

applyBranchRenamed(event: BranchRenamed): void {
this._id = event.aggregateId
this.name = event.name
}

rename(name: string): void {
if (!name) throw new MissingParameterError('Must provide a name')
this.applyChange(new BranchRenamed(this._id, name))
}
}
19 changes: 19 additions & 0 deletions src/domain/branch/BranchCommandHandlers.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
import {IRepository} from '../../es-cqrs/Repository'
import {Branch} from './Branch'
import {CreateBranch, RenameBranch} from './BranchCommands'
import {guid} from '../../es-cqrs/Guid'

export class BranchCommandHandlers {
constructor(private _repository: IRepository<Branch>) {}

handleCreateBranch(command: CreateBranch): void {
const branch = new Branch(guid(), command.name)
this._repository.save(branch, -1)
}

handleRenameBranch(command: RenameBranch): void {
const branch = this._repository.getById(command.aggregateId)
branch.rename(command.name)
this._repository.save(branch, command.expectedAggregateVersion)
}
}
17 changes: 17 additions & 0 deletions src/domain/branch/BranchCommands.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
import {Command} from '../../es-cqrs/Command'

export class CreateBranch extends Command {
constructor(public readonly name: string) {
super(-1)
}
}

export class RenameBranch extends Command {
constructor(
public readonly aggregateId: string,
public readonly name: string,
public readonly expectedAggregateVersion: number
) {
super(expectedAggregateVersion)
}
}
19 changes: 19 additions & 0 deletions src/domain/branch/BranchEvents.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
import {Event} from '../../es-cqrs/Event'

export class BranchCreated extends Event {
constructor(
public readonly aggregateId: string,
public readonly name: string
) {
super(aggregateId)
}
}

export class BranchRenamed extends Event {
constructor(
public readonly aggregateId: string,
public readonly name: string
) {
super(aggregateId)
}
}
Loading

0 comments on commit a872f35

Please sign in to comment.