Building JavaScript applications based on DDD, CQRS and EventSourcing

Post on 01-Dec-2014

148 Views

Category:

Software

2 Downloads

Preview:

Click to see full reader

DESCRIPTION

Video: http://www.youtube.com/watch?v=XSc7NPedAxw&feature=youtu.be Model-View-Controller (MVC) was born in the 1970s and is still one of the most used architectural patterns in the software world. On top of that it's common to connect Data-Models using Object-relational Mapping (ORM) to a Database and provide easy Create-Read-Update-Delete (CRUD) Routes as an API for applications. In this talk I will show an alternative approach and how to easily apply it using JavaScript. Live coding included. In the process the audience will learn about Domain-driven Design (DDD), Command Query Responsibility Seggregation (CQRS), EventSourcing and how to explicitly capture your domain events - something the ThoughtWorks TechnologyRadar2014 also outlined as an emerging technique, because it makes developers and business people happy.

Transcript

Building JavaScript applicationsbased on DDD, CQRS and EventSourcing

MunichJS, 04. Sept 2014

DomainDomain Model Model

ORMORM

CRUDCRUD

Updates Manipulates

Sees Uses

Our Domain

We are a SchoolSchoolWe have StudentsStudents and CoursesCourses

Students should be able to- join a Course- leave a Course

var studentSchema = new MongooseSchema({ name: String});

var courseSchema = new MongooseSchema({ name: String});

Relationships with ORM

Students should be able to join Courses

var courseSchema = new MongooseSchema({ name: String, students: [ studentSchema ]});

app = express()

// C(reate)app.post('/course/create', course.create);

// R(ead)app.get('/course', course.read);app.get('/course/:id', course.read);

// U(pdate)app.post('/course/update/:id', course.update);

// D(elete)app.get('/course/delete/:id', course.delete);

Where did theWhere did theBusiness-Logic go?Business-Logic go?

Business -

Reporting

Which Students first joined a Course but left it again?

var courseSchema = new MongooseSchema({ name: String, students: [ studentSchema ], studentsLeftIds: Array});

// U(pdate) Routecourse.update = function(courseId, updatedCourse) { currentCourse = course.findById(courseId);

// U(pdate) Routecourse.update = function(courseId, updatedCourse) { currentCourse = course.findById(courseId);

// add students to "studentsLeft" that left the course currentCourse.studentsLeftIds = calculateStudentsLeftTheCourse(currentCourse,updatedCourse);

// save currentCourse.save()};

// U(pdate) Routecourse.update = function(courseId, updatedCourse) { currentCourse = course.findById(courseId);

// remove students from "studentsLeft" that enrolled again currentCourse.studentsLeftIds = calculateStudentsJoinedTheCourseAgain(currentCourse, upatedCourse);

// add students to "studentsLeft" that left the course currentCourse.studentsLeftIds = calculateStudentsLeftTheCourse(currentCourse, updatedCourse);

// save currentCourse.save()};

Another approachAnother approach

BoundedContext

Encapsulates your DomainModel(s)

var school = eventric.context('school');

Ubiquitous Language

Behaviors in your Domain

EventStormingEventStorming

DomainEventsDomainEvents

school.defineDomainEvents({ StudentJoinedCourse: function(params) { this.courseId = params.courseId; }, StudentLeftCourse: function(params) { this.courseId = params.courseId; }});

Aggregate

school.addAggregate('Student', function() { this.joinInCourse = function(courseId) { this.$emitDomainEvent('StudentJoinedCourse', { courseId: params.courseId }); }

school.addAggregate('Student', function() { this.joinInCourse = function(courseId) { this.$emitDomainEvent('StudentJoinedCourse', { courseId: params.courseId }); } this.leaveCourse = function(courseId) { this.$emitDomainEvent('StudentLeftCourse', { courseId: params.courseId }); }});

CommandHandler

school.addCommandHandlers({

StudentJoinCourse: function(params, done) { studentRepository = this.$repository('Student'); studentRepository.findById(params.studentId) .then(function(student) {

student.joinCourse(params.courseId);student.joinCourse(params.courseId);

studentRepository.save(params.studentId); }) .then(function() { done(); }) }

StudentLeaveCourse: function(params, done) { studentRepository = this.$repository('Student'); studentRepository.findById(params.studentId) .then(function(student) {

student.leaveCourse(params.courseId);student.leaveCourse(params.courseId);

studentRepository.save(params.studentId); }) .then(function() { done(); }) }

});

Reporting using Projections

school.addProjection(function() { this.studentsLeft = {}

this.handleStudentLeftCourse = function(domainEvent) { // make sure the object has the correct format this.studentsLeft[domainEvent.aggregate.id] .course[domainEvent.payload.courseId] = true; } });

school.addProjection(function() { this.studentsLeft = {}

this.handleStudentJoinedCourse = function(domainEvent) { // make sure the object has the correct format delete this.studentsLeft[domainEvent.aggregate.id] .course[domainEvent.payload.courseId] }

this.handleStudentLeftCourse = function(domainEvent) { // make sure the object has the correct format this.studentsLeft[domainEvent.aggregate.id] .course[domainEvent.payload.courseId] = true; } });

CQRS + EventSourcingCQRS + EventSourcing

to the Livecoding..to the Livecoding..

Johannes Becker@dieserjohannes

eventricjs.orgeventricjs.org

github.com/efacilitation/eventricgithub.com/efacilitation/eventricgithub.com/efacilitation/eventric-todoMVCgithub.com/efacilitation/eventric-todoMVC

top related