Slices across different base packages
I'd like to cut slices in the following package structure:
- application
- a
- http
- grpc
- b
- kafka
- sns
- a
- domain
- api
- a
- b
- logic
- a
- b
- spi
- c
- d
- api
- infrastructure
- c
- memory
- postgres
- d
- file
- s3
- c
Slices that I'd like to have:
-
application.a.http,application.a.grpc,domain.api.aanddomain.logic.a -
application.b.kafka,application.b.sns,domain.api.banddomain.logic.b -
domain.spi.c,infrastructure.c.memoryandinfrastructure.c.postgres -
domain.spi.d,infrastructure.d.fileandinfrastructure.d.s3
If I could specify multiple package identifiers (when calling slices().matching(packageIdentifier)), then I would use (slice 1+2):
"..application.(*).*.."
"..domain.api.(*).."
"..domain.logic.(*).."
or respectively (slice 3+4):
"..domain.spi.(*).."
"..infrastructure.(*).*.."
Is there a readable way to express that with a single package identifier expression?
Alternatively I'm currently using this (Kotlin) implementation that supports multiple package identifiers:
private fun SlicesRuleDefinition.Creator.matching(vararg packageIdentifiers: String) =
assignedFrom(MultiPackageMatchingSliceIdentifier(*packageIdentifiers))
private class MultiPackageMatchingSliceIdentifier(private vararg val packageIdentifiers: String) : SliceAssignment {
override fun getIdentifierOf(javaClass: JavaClass): SliceIdentifier =
packageIdentifiers
.map {
PackageMatcher.of(it)
.match(javaClass.packageName)
.map(TO_GROUPS)
.orElse(emptyList())
}
.filter { it.isNotEmpty() }
.let { it.flatten() }
.let { parts -> identifierOf(parts) }
private fun identifierOf(parts: List<String>?) =
if (parts.isNullOrEmpty()) ignore() else of(parts)
override fun getDescription(): String {
return packageIdentifiers.joinToString(", ") { "'$it'" }
}
}
I don't know if I completely understand your example, because e.g. "..application.(*).*.." would not give you slices application.a.http and application.a.grpc but only application.a and application.b, no?
The packageIdentifier API at the moment works if a) all your relevant packages have the same level of nesting (e.g. relevant packages are always level 3 and 4) and b) you don't need to ignore any of the matched packages.
Then you could for example also do
com.myapp.(*).(*).(*)..
(given com.myapp is your root package) and match your example to derive pretty much exactly the slices you want. The first asterisk would match application, domain, infrastructure, the second the first subpackage, etc. A slice would be defined by exactly the same triple.
If your structure is not so homogeneous, then so far you actually have to do it the SliceAssignment way that you have already found. We could add an API for multiple patterns, but I'm not sure yet, if it is really useful for real life use cases. My fear would be that it is still too weak for most cases and users will have to fall back onto SliceAssignment anyway.
because e.g.
"..application.(*).*.."would not give you slicesapplication.a.httpandapplication.a.grpcbut onlyapplication.aandapplication.b, no?
What I wanted to say there was:
Slices that I'd like to have:
- Slice
a, containing packages:application.a.http,application.a.grpc,domain.api.aanddomain.logic.a- Slice
b, containing packages:application.b.kafka,application.b.sns,domain.api.banddomain.logic.b- Slice
c, containing packages:domain.spi.c,infrastructure.c.memoryandinfrastructure.c.postgres- Slice
d, containing packages:domain.spi.d,infrastructure.d.fileandinfrastructure.d.s3
Ah, okay, I see. You're right, at the moment the simple API works best for the cases where the business cuts are toplevel and the technical cuts are subpackages. For the other way round, I think the only way at the moment is SliceAssignment, which you already found.
Maybe it would be good to support multiple patterns out of the box, because that would cover exactly the case where it is a consistent structure, but technical cuts toplevel :thinking:
Hi. If I understand the question correctly this could benefit from the feature request I made some times ago: https://github.com/TNG/ArchUnit/issues/662 . maybe it makes sense to look at both topics together.