Skip to content

Instantly share code, notes, and snippets.

@equivalent
Created August 21, 2020 09:46
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save equivalent/37845308ca12097a11152aae63399caa to your computer and use it in GitHub Desktop.
Save equivalent/37845308ca12097a11152aae63399caa to your computer and use it in GitHub Desktop.
Ruby on Rails - Bounded contexts via interface objects - Bounded Contexts example 1
# config/application.rb
module MyApplication
class Application < Rails::Application
# ...
# We need to tell Rails to include `app/bounded_contexts` in auto loader
# This step is needed only in older Rails applications.
# Any subdirs you put in `./app` should automatically get picked up as an autoload path in newer Rails
config.autoload_paths << Rails.root.join('app', 'bounded_contexts')
# ...
end
end
# app/bounded_contexts/classroom/lesson_creation_service.rb
module Classroom
module LessonCreationService
extend self
def call(students:, teacher:, title:)
lesson = Lesson.create!(title: title, teacher: teacher)
students.each do |student|
lesson.students << student
notify_student(student, lesson)
end
lesson
end
private
def notify_student(student, lesson)
Classroom::StudentMailer
.invitation_to_lesson(lesson_id: lesson.id, student_id: student.id)
.deliver_later
end
end
end
# app/bounded_contexts/classroom/lesson_interface.rb
module Classroom
class LessonInterface
attr_reader :lesson
def initialize(lesson)
@lesson = lesson
end
def upload_work(student:, file:)
Classroom::WorkUploadService.call(student: student, lesson: lesson, file: file)
end
def publish
lesson.published = true
lesson.save!
end
def cross_boundary_example
# some logic related to classroom bounded context
end
end
end
# app/bounded_contexts/classroom/reprocess_work_thumbnail_job.rb
module Classroom
class ReprocessWorkThumbnailJob < ActiveJob::Base
queue_as :classroom
def perform(work_id:)
work = Work.find_by!(id: work_id)
# do something with the work
end
end
end
# app/bounded_contexts/classroom/student_mailer.rb
class Classroom::StudentMailer < ApplicationMailer
def invitation_to_lesson(lesson_id:, student_id:)
@lesson = Lesson.find_by!(id: lesson_id)
@student = Student.find_by!(id: student_id)
mail(to: @student.email, subject: %{New lesson "#{@lesson.title}"})
end
end
# app/bounded_contexts/classroom/teacher_interface.rb
module Classroom
class TeacherInterface
attr_reader :teacher
def initialize(teacher)
@teacher = teacher
end
def create_lesson(students:, title:)
Lesson.transaction do
Classroom::LessonCreationService.call(students: students, title: title, teacher: teacher)
end
end
end
end
# app/bounded_contexts/classroom/teacher_mailer.rb
module Classroom
class TeacherMailer < ApplicationMailer
def new_work_uploaded_to_lesson(work_id:, teacher_id:)
@work = Work.find_by!(id: work_id)
@teacher = Teacher.find_by!(id: teacher_id)
mail(to: @teacher.email, subject: %{New work was added "#{@work.id}"})
end
end
end
# app/bounded_contexts/classroom/work_upload_service.rb
module Classroom
module WorkUploadService
extend self
def call(student:, lesson:, file:)
work = lesson.works.new(student: student)
work.file = file
work.save!
notify_teacher(work, teacher)
schedule_reprocess_work_thumbnail(work)
work
end
private
def notify_teacher(work, teacher)
Classroom::TeacherMailer
.new_work_uploaded_to_lesson(work_id: work.id, teacher_id: teacher.id)
.deliver_later
end
def schedule_reprocess_work_thumbnail(work)
Classroom::ReprocessWorkThumbnailJob.perform_later(work_id: work.id)
end
end
end
# app/bounded_contexts/public_board/comment_posted_job.rb
module PublicBoard
class CommentPostedJob < ActiveJob::Base
queue_as :public_board
def perform(comment_id:)
comment = Comment.find_by!(id: comment_id)
# do something with the comment
end
end
end
# app/bounded_contexts/public_board/lesson_interface.rb
module PublicBoard
class LessonInterface
attr_reader :lesson
def initialize(lesson)
@lesson = lesson
end
def mark_as_favorite(current_user:)
# ... some logic
end
def cross
# demonstration of how you can call other bounded context from this bounded context
result = lesson.classroom.cross_boundary_example
# ... you can use the result of different boundary in this boundary
end
end
end
# app/bounded_contexts/public_board/student_mailer.rb
module PublicBoard
class StudentMailer < ActiveJob::Base
def new_comment_on_your_work(comment_id:)
comment = Comment.find_by!(id: comment_id)
# ...
end
end
end
# app/bounded_contexts/public_board/work_interface.rb
module PublicBoard
class WorkInterface
attr_reader :work
def initialize(work)
@work = work
end
def can_post_comment?(current_user:)
work.lesson.published && current_user.is_a?(Student)
end
def post_comment(student:, content:)
comment = @work.comments.create!(student: student, content: content)
PublicBoard::CommentPostedJob.perform_later(comment_id: comment.id)
PublicBoard::StudentMailer.new_comment_on_your_work(comment_id: comment.id).deliver_later
comment
end
end
end