I've summarized what's popular in me these days.
What was originally prepared as an operation on a set is ** too fine or too specific **, and it is difficult to understand what it means to the user of the software. Here, for a set We will summarize the operations at a granularity that is meaningful to the user.
Various methods are defined in advance in the class (list or map) that represents a set. However, there may be methods that you do not want to use in your business logic. It is not necessary to write to the set. On the contrary, the state where it can be written may cause a bug. The implementation method introduced here can be used to limit the operations to the set.
Assume a web application that aggregates the number of events (Push events) that occur on GitHub for each repository and shares it on Twitter.
Assume a multi-module configuration in which the following are divided into modules.
Module name | Description |
---|---|
web | There is a web package that handles the IO of the browser and an application package that realizes the specifications of the application.. |
domain | Express business logic by entities and models. |
infrastructure | Implement the interface defined in the domain layer. |
The code implements the logic to aggregate the number of Events that occurred on GitHub for each Git repository. The code is
The GitEvent class is defined as follows.
domain.GitEvent.scala
class GitEvent(val gitRepository: GitRepository, val eventType: GitEventType)
You can also get a collection of GitEvents through the getUserEvents method of GitEventClient.
domain.client.GitEventClient.scala
trait GitEventClient {
def getUserEvents(gitAccount: GitAccount): Seq[GitEvent]
}
At first, I used the application service to perform aggregation. Perhaps even in business, the application service often calls the method of the data access layer and processes the acquired data.
web.application.GitActivitySummaryService.scala
class GitActivitySummaryService(){
def summarize(userId: UserId): Seq[GitActivitySummary] = {
val accountService: GitAccountService = new GitAccountService()
val gitAccount = accountService.getByUserId(userId)
//Get events based on the Git account information you got.
val eventClient: GitEventClient = new GitHubEventClient()
val gitEvents: Seq[GitEvent] = eventClient.getUserEvents(gitAccount)
//Group by repository,Repository information,Generate a collection of instances with the number of times
events.groupBy(e => e.gitRepository).map(e => new GitActivitySummary(e._1, e._2.size)).toSeq
}
}
The biggest problem with this code is that the event aggregation logic can be implemented in different places. For example, until now, only real-time aggregation was performed on the Web, but what should we do if the following request arises?
Since the permissions to resources are different between the web application and batch, we decided to create a batch
module.
Although batch requires the same aggregation logic as the web application, it cannot be referenced from batch because the logic was written in the web module when the web application was implemented.
I "I can't help it, do I copy the same class to the batch module?" Everyone "Wait, wait, wait."
I'm trying to automate the aggregation of Git events, so I think the aggregation logic is the biggest concern in this app. That's why I created the GitEvents
class in the domain
module. ..
domain.gitEvents.scala
class GitEvents (private val events: Seq[GitEvent]) {
def countByRepository() = {
val gitActivitySummaries = events.groupBy(e => e.gitRepository).map(e => new GitActivitySummary(e._1, e._2.size)).toSeq
new GitActivitySummaries(gitActivitySummaries)
}
}
Since GitEventClient also returns an instance of the GitEvents class, Seq [GitEvent] can no longer be operated directly outside the GitEvents class. Object-oriented encapsulation may be something like this.
domain.client.GitEventClient.scala
trait GitEventClient {
def getUserEvents(gitAccount: GitAccount): GitEvents
}
If you expose the method of the field collection via the wrapper class as shown below, you can operate it as if you were dealing with the field collection directly.
def foreach(f: GitEvent => Unit) = events.foreach(f)
def map[T](f:GitEvent=>T)=events.map(f)
If you need to implement other methods defined in the collection of fields, you can provide the API to the calling class as if you were working directly with the collection. You can also write a for expression by implementing the iterator method.
def iterator = events.iterator
for ( event <- events) {
println(event.eventType)
}
Recommended Posts