N+1使
使

--
CREATE TABLE students (
id SERIAL NOT NULL,
name VARCHAR(100) NOT NULL,
PRIMARY KEY(id)
);
--
CREATE TABLE courses (
id SERIAL NOT NULL,
name VARCHAR(100) NOT NULL,
PRIMARY KEY(id)
);
--
CREATE TABLE enrollments (
student_id BIGINT NOT NULL,
course_id BIGINT NOT NULL,
PRIMARY KEY(student_id, course_id)
)
FK
1
*
FK
*
1
«table»
students
+id : SERIAL [PK]
+name : VARCHAR(100)
«table»
courses
+id : SERIAL [PK]
+name : VARCHAR(100)
«table»
enrollments
+student_id : BIGINT [PK,FK]
+course_id : BIGINT [PK,FK]
1.
2. ID?
3. ?
4. ?

4


1:
? ? ? ?
:
Modeling Relationships in a DDD way - DDD
Change Bidirectional Association to Unidirectional - Martin Fowler

record Student(
Identity id,
String name,
List<Course> courses) {
}
record Course(
Identity id,
String name,
List<Student> students) {
}
enrolls
0..*
0..*
Student
+Identity id
+String name
+List<Course> courses
Course
+Identity id
+String name
+List<Student> students
Pros:
student.getCourses()course.getStudents()
辿
Cons:
JSON/XML
Eager
使
Ruby on RailsActiveRecordJavaJPAGraphQL?

StudentCourseCourseStudent
record Student(
Identity id,
String name,
List<Course> courses) {
}
record Course(
Identity id,
String name) {
}
//
student.courses()
enrolls
0..*
0..*
Student
+Identity id
+String name
+List<Course> courses
Course
+Identity id
+String name
Pros:
使
Cons:
Course
(3)
?

EnrollmentStudentCourseEnrollment
record Student(String name) {
}
record Course(String name) {
}
record Enrollment(Student student, Course course) {
}
Student
+String name
Course
+String name
Enrollment
+Student student
+Course course
Pros:
Cons:
?

Modeling Relationships in a DDD way使()

2: ID
IDID
:
Link to an aggregate: reference or Id? - ID
Rule: Reference Other Aggregates by Identity - Vaughn VernonID
Vaughn Vernon - Effective Aggregate Design Part II - ID

enrolls
0..*
0..*
Student
+Identity id
+String name
+List<Course> courses
Course
+Identity id
+String name
+List<Student> students
Pros:
student.courses.forEach(...)
Cons:
使

ID
ID
Student
+Identity id
+String name
+List<Identity> courseIds
Course
+Identity id
+String name
+List<Identity> studentIds
record Student(
Identity id,
String name,
List<Identity> courseIds) {
}
record Course(
Identity id,
String name,
List<Identity> studentIds) {
}
void enroll(Student student, Identity courseId) {
}
//
//
List<Course> listCourse(Long studentId) {
return select().from("courses")
.join(table("enrollments"))
.on(table("enrollments").field("course_id").eq(table("courses").field("id")))
.where(table("enrollments").field("studentId").eq(studentId))
.fetch()
.map(courseMapper::toDomain)
}
Pros:
ID
Cons:
ID使
> DDDBounded ContextID使使

3: ?
:
Domain model purity and lazy loading -
Loading Related Data - EF Core - 使
Patterns of Enterprise Application Architecture - Lazy Load - Martin Fowler

Eager Loading
record Course(
Identity id,
String name,
List<Student> students) {
}
has (loaded)
1
0..*
Course
+Identity id
+String name
+List<Student> students
Student
+Identity id
+String name
studentsは常に実体化されている
Pros:
DB
N+1
Cons:
使
使

Nullable
null
record Course(
Identity id,
String name,
List<Student> students) { // null or loaded
}
has (nullable)
1
0..*
Course
+Identity id
+String name
+List<Student>? students
Student
+Identity id
+String name
studentsはnullの可能性がある
Pros:
使
使
Cons:
null
null
NullPointerException
null
null

Lazy Load
Martin FowlerPatterns of Enterprise Application Architecture - Lazy Load
record Course(
Identity id,
String name,
List<Student> students) {
public List<Student> students() {
if (this.students instanceof VirtualList vl) {
vl.load();
}
return this.students;
}
)
has (lazy)
1
0..*
Course
+Identity id
+String name
+VirtualList<Student> students
+students() : List<Student>
Student
+Identity id
+String name
studentsは初回アクセス時にロード
Pros:
null
Cons:
ORM
DB

4:
:
The Unit Of Work Pattern And Persistence Ignorance - Unit of Work
Domain modeling with Entity Framework scorecard - Entity FrameworkNHibernate

SQL
Ruby on RailsActive RecordSQL
Pros:
SQL11
Cons:
DB

Unit of Work
JPAHibernateEntityflush/commitSQL
Pros:
Cons:
ORM
使
flush
LazyLoading

INSERT/DELETE
Pros:
Cons:

DEL/INS
DELETEINSERT
Pros:
Cons:
DELETE/INSERT

4使Eager Load使NullableLazy Load

1. - - Lazy Load - or Unit of Work
Lazy LoadEager
: RailsActiveRecord JavaJPA
2. - - Nullable - *
GraphQL
/
3. or - * - Eager Load - or DEL/INS

3


2

111

RDB

record Restaurant(
Identity id,
String name,
List<Table> tables
) {}
record Table(
Identity id,
int tableNumber,
int capacity
){}
void save(Restaurant restaurant) {
updateRestaurant(restaurant);
//
save
}

FK
1
*
FK
*
1
«table»
users
+id : SERIAL [PK]
+name : VARCHAR(100)
«table»
groups
+id : SERIAL [PK]
+name : VARCHAR(100)
«table»
memberships
+user_id : BIGINT [PK,FK]
+group_id : BIGINT [PK,FK]
usergroupmembership



record User(Identity id, String name) {}
record Group(Identity id, String name) {}
record Membership(User user, Group group) {}
1group10 users
user
record Group(List<User> users) {
void join(User user) {
if (users.size() > 10) {
throw new OverException(10);
}
users.add(user);
}
}
()

Membership
Membership join(Group group, User user) {
int numOfMembers = groupRepository.countMembers(group.id());
if (numOfMembers > 10) {
throw new OverException(10);
}
return Membership.of(user, group);
}

()
record Order(Identity id,
Customer customer,
Product product,
LocalDateTime orderedAt) {}
if (!productRepository.exists(req.productId())) {
throw new ProductNotFoundException(req.productId);
}
// ...
使 (Parse don't validate)
Optional<Product> product = productRepository.findById(req.productId());
product.ifPresent(p -> {
Order order = new Order(
id, customer, product, orderedAt
);
orderRepository.save(order);
});

-
record Order(Identity id,
Customer customer,
List<OrderLine> lines
LocalDateTime orderedAt) {}
record OrderLine(
Product product,
int amount
) {}
DDD使

(10)使Eager Load

ID

1111
請求する
1
0..1
注文
+注文ID
+注文日時
請求
+請求ID
+注文
+請求日時
ID
record Order {
Identity id,
LocalDateTime orderedAt
}
record Invoice {
Identity id,
Identity orderId, //
LocalDateTime invoicedAt
}
record Order {
Identity id,
LocalDateTime orderedAt
}
record Invoice {
Identity id,
List<Identity> orderIds, //
LocalDateTime invoicedAt
}
IDID