Building JavaScript applications based on DDD, CQRS and EventSourcing MunichJS, 04. Sept 2014
Dec 01, 2014
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