Skip to content

Instantly share code, notes, and snippets.

@j5ik2o
Last active July 8, 2024 15:35
Show Gist options
  • Save j5ik2o/1b9041f25af43818060928aad554d3cd to your computer and use it in GitHub Desktop.
Save j5ik2o/1b9041f25af43818060928aad554d3cd to your computer and use it in GitHub Desktop.
// 雑に書いているので、あろうことかエラーメッセージは日本語です。
import scala.collection.mutable
// Domain Layer (unchanged)
case class ProductId(value: String)
case class ShippingProviderId(value: String)
enum ProductStatus:
case Draft, Published
case class Product(
id: ProductId,
name: String,
status: ProductStatus,
shippingProviderId: Option[ShippingProviderId]
)
enum DomainError:
case BusinessRuleViolation(message: String)
case RepositoryError(message: String)
// Purely Functional Domain Service
object ProductPublicationDomainService:
private def canPublish(product: Product): Boolean =
product.shippingProviderId.isDefined
private def publishProduct(product: Product): Either[DomainError, Product] =
if canPublish(product) then
Right(product.copy(status = ProductStatus.Published))
else
Left(DomainError.BusinessRuleViolation(s"配送業者が設定されていません: ${product.id.value}"))
def publishProducts: LazyList[Product] => LazyList[Either[DomainError, Product]] =
_.map(publishProduct)
// Repository Interface
trait ProductRepository:
def findDraftProducts(): LazyList[Product]
def saveProducts(products: Seq[Product]): Either[DomainError, Unit]
// In-Memory Repository Implementation (Mutable Object)
class InMemoryProductRepository extends ProductRepository:
private val products = mutable.Map[ProductId, Product]()
def addProduct(product: Product): Unit =
products += (product.id -> product)
def findDraftProducts(): LazyList[Product] =
LazyList.from(products.values).filter(_.status == ProductStatus.Draft)
def saveProducts(products: Seq[Product]): Either[DomainError, Unit] =
try
products.foreach(p => this.products += (p.id -> p))
Right(())
catch
case e: Exception => Left(DomainError.RepositoryError(s"バッチ保存中にエラーが発生しました: ${e.getMessage}"))
// Application Layer
case class PublishProductsResult(
successCount: Int,
failureCount: Int,
errors: LazyList[DomainError]
)
object PublishProductsUseCase:
def execute(
productRepository: ProductRepository,
batchSize: Int = 1000
): LazyList[PublishProductsResult] =
ProductPublicationDomainService.publishProducts(productRepository.findDraftProducts())
.grouped(batchSize)
.map { batch =>
val (successes, failures) = batch.partition(_.isRight)
val successProducts = successes.collect { case Right(product) => product }
val saveResult = productRepository.saveProducts(successProducts)
PublishProductsResult(
successCount = if saveResult.isRight then successProducts.size else 0,
failureCount = failures.size + (if saveResult.isLeft then successProducts.size else 0),
errors = failures.collect { case Left(error) => error } ++ saveResult.left.toSeq
)
}.to(LazyList)
// Usage example
@main def run(): Unit =
// リポジトリの初期化とサンプルデータの追加
val repository = new InMemoryProductRepository()
(1 to 1000000).foreach { i =>
repository.addProduct(
Product(
ProductId(s"P$i"),
s"Product $i",
ProductStatus.Draft,
if i % 3 == 0 then Some(ShippingProviderId(s"SP$i")) else None
)
)
}
println("リポジトリにサンプルデータを追加しました。処理を開始します。")
// Execute use case
val results = PublishProductsUseCase.execute(repository)
// Process and print results
var totalSuccess = 0
var totalFailure = 0
var totalErrors = List.empty[DomainError]
results.zipWithIndex.foreach { case (result, index) =>
totalSuccess += result.successCount
totalFailure += result.failureCount
totalErrors = totalErrors ++ result.errors.take(5) // 各バッチから最大5つのエラーを記録
// 10万件ごとに中間結果を表示
if (index + 1) * 1000 % 100000 == 0 then
println(s"Processed ${(index + 1) * 1000} products")
println(s"Current success: $totalSuccess, Current failure: $totalFailure")
}
println(s"""
|最終処理結果:
| 成功: $totalSuccess
| 失敗: $totalFailure
| エラーサンプル (最大15件):
| ${totalErrors.take(15).mkString("\n ")}
""".stripMargin)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment