Date post: | 01-Dec-2014 |
Category: |
Software |
Upload: | johannes-becker |
View: | 148 times |
Download: | 2 times |
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