Solution: CommentService

Simple solution:

class CommentService( private val commentRepository: CommentRepository, private val userService: UserService, private val commentFactory: CommentFactory ) { suspend fun addComment( token: String, collectionKey: String, body: AddComment ) { val userId = userService.readUserId(token) val commentDocument = commentFactory .toCommentDocument(userId, collectionKey, body) commentRepository.addComment(commentDocument) } suspend fun getComments( collectionKey: String ) = coroutineScope { val commentDocuments = commentRepository .getComments(collectionKey) CommentsCollection( collectionKey = collectionKey, elements = commentDocuments .map { async { makeCommentElement(it) } } .awaitAll() ) } private suspend fun makeCommentElement( commentDocument: CommentDocument ) = CommentElement( id = commentDocument._id, collectionKey = commentDocument.collectionKey, user = userService.findUserById(commentDocument.userId), comment = commentDocument.comment, date = commentDocument.date, ) }

This is how this class should look if we want to make sure that findUserById is not called more than once for the same user id by the same getComments call:

class CommentService( private val commentRepository: CommentRepository, private val userService: UserService, private val commentFactory: CommentFactory ) { suspend fun addComment( token: String, collectionKey: String, body: AddComment ) { val userId = userService.readUserId(token) val commentDocument = commentFactory .toCommentDocument(userId, collectionKey, body) commentRepository.addComment(commentDocument) } suspend fun getComments( collectionKey: String ) = coroutineScope { val commentDocuments = commentRepository .getComments(collectionKey) val users: Map<String, User> = commentDocuments .map { it.userId } .toSet() .map { async { userService.findUserById(it) } } .awaitAll() .associateBy { it.id } CommentsCollection( collectionKey = collectionKey, elements = commentDocuments.map { val user = users[it.userId] makeCommentElement(it, user) } ) } private fun makeCommentElement( commentDocument: CommentDocument, user: User?, ) = CommentElement( id = commentDocument._id, collectionKey = commentDocument.collectionKey, user = user, comment = commentDocument.comment, date = commentDocument.date, ) }

Example solution in playground

import domain.comment.* import kotlinx.coroutines.async import kotlinx.coroutines.awaitAll import kotlinx.coroutines.coroutineScope class CommentService( private val commentRepository: CommentRepository, private val userService: UserService, private val commentFactory: CommentFactory ) { suspend fun addComment( token: String, collectionKey: String, body: AddComment ) { val userId = userService.readUserId(token) val commentDocument = commentFactory .toCommentDocument(userId, collectionKey, body) commentRepository.addComment(commentDocument) } suspend fun getComments( collectionKey: String ) = coroutineScope { val commentDocuments = commentRepository .getComments(collectionKey) CommentsCollection( collectionKey = collectionKey, elements = commentDocuments .map { async { makeCommentElement(it) } } .awaitAll() ) } private suspend fun makeCommentElement( commentDocument: CommentDocument ) = CommentElement( id = commentDocument._id, collectionKey = commentDocument.collectionKey, user = userService.findUserById(commentDocument.userId), comment = commentDocument.comment, date = commentDocument.date, ) }