mobile it

 

 

Application domain modelinghttps://mobileit.cz/Blog/Pages/application-domain-modeling.aspxApplication domain modeling<p>Software projects are about telling computers what people want to do. Programmers are the ones who speak the computer language, but ordinary people aren't predestined to think and communicate the way a computer does. They are usually good at telling what they want to achieve and what is the purpose. Then it's the programmer's task to translate the purpose into computer language.</p><p>This arrangement leads to an unintended limitation. When a person needs to tell something to a computer, they need a programmer. When people need information about computer behavior, they need to ask a programmer. Programmers are a bottleneck in this process.</p><p>But what if we can design the project so the code is understandable even for ordinary people?</p><h2>Enter the domain</h2><p>A domain is a part of every project that does the important things. The business logic resides there together with industry rules and policies. The domain must be articulated unambiguously for everyone.</p><p>The domain shouldn't be too technical. There should not be transactions, exceptions, wrappers, or gateways. Ordinary people do not understand these terms but need to understand the domain. Although programmers understand these technicalities, they are ambiguous, and two programmers may imagine a different mechanism under these terms.</p><p>The domain should be full of real-world objects. We should see purchase orders, money amounts, other people, or actual goods. These objects can be virtual, like card transactions, but need to be recognizable to everyone.</p><p>Kotlin exhorts for readable code. This is one of its main characteristics. And we can use it to model our domain.</p><p>The real-world objects are usually modeled as <span class="pre-inline">data class</span> with read-only properties. Any modification needs to be done by a generated <span class="pre-inline">copy()</span> function that prevents unintended state updates or inconsistencies.</p><p>Some objects or data items like Social Security Numbers are so simple that one may tend to represent them as a primitive type like <span class="pre-inline">String</span> or <span class="pre-inline">Int</span>. This is good for program efficiency, but a person may unintentionally swap it with some other information of the same primitive type and introduce a severe error. To overcome this, we may introduce a <span class="pre-inline">data class</span> to wrap the information and give it a unique type. This will work, but its overhead may consume an unacceptable amount of resources when used at a larger scale. Kotlin <span class="pre-inline">value class</span> is a great fit for such use cases. It assigns a unique type to a simple data item while still keeping its memory footprint small.</p><h2>Domain actions</h2><p>Domain objects are important, but they are useless unless we can take action with them. A purchase order is to be placed or canceled. A parcel with a new pair of shoes is to be shipped or claimed. And we need to model these actions as well.</p><p>The first thing is the name. <span class="pre-inline">Action</span> in programming is quite an overused, and generic term and it does not work well for us. Some teams name them <span class="pre-inline">Interactor</span> others use the term <span class="pre-inline">UseCase</span>, or <span class="pre-inline">Actor</span>. We will use the term <span class="pre-inline">UseCase</span> for the rest of this post.</p><p>A use case is defined as a single function with explicit input and output. Use cases have no state and can be called multiple times. Let's try to define them in Kotlin. </p><p>It would be nice if a <span class="pre-inline">UseCase</span> may have a single input and single output so we can define a base type for all use cases with inputs and outputs defined as generics. But in the real world there are actions that may have more inputs, so this is not feasible, is it?</p><pre><code class="kotlin hljs">class FindDriverUseCase { fun execute(position: Coordinates, destination: Coordinates) } </code></pre><p>This approach will work technically, but it does not tell the whole story for people unfamiliar to the context.</p><p>Introducing a specific type for use case input will actually help to understand the context and works as an extension point for further feature evolution.</p><pre><code class="kotlin hljs">class FindDriverUseCase { fun execute(input: PotentialRide) } </code></pre><p>Great, after using this approach for a while, you'll realize that some use cases have their input and output optional. That's fine, Kotlin has built-in nullability in the type system, doesn't it?</p><pre><code class="kotlin hljs">class FindStoresUseCase { fun execute(input: Country?): List<Store>? } </code></pre><p>The use case finds all our company stores. We may want to get a list of all of them or to filter them by a given <span class="pre-inline">Country</span>.</p><p>The definition of input as <span class="pre-inline">null</span> on the use-site is not very understandable in this case. Also, it is hard to guess what <span class="pre-inline">null</span> as returned result actually means. A little bit more modeling will fix this:</p><pre><code class="kotlin hljs">sealed interface For { object AllCountries: For data class SingleCountry(val country: Country): For } sealed interface StoresResult { object NotAvailable: StoresResult object OutOfSync: StoresResult data class Available(val stores: List<Store>): StoresResult } class FindStoresUseCase { fun execute(input: For): StoresResult } </code></pre><p>It seems to be a good idea to deny the value <span class="pre-inline">null</span> as use case input or output at all. Let's introduce a base type and force the generics to be based on <span class="pre-inline">Any</span> that effectively forbids <span class="pre-inline">null</span>: </p><pre><code class="kotlin hljs">interface UseCase<in Input: Any, out Output: Any> { fun execute(input: Input): Output } </code></pre><h2>Actions namespace</h2><p>Quite often, we have a few use cases working with similar objects:</p><p><span class="pre-inline">SubmitPurchaseOrderUseCase</span>, <span class="pre-inline">CancelPurchaseOrderUseCase</span>, …</p><p>These class types may quickly pollute the project's namespace and make it hard to navigate.</p><p>We should try to group them somehow under a shared namespace. Kotlin doesn't have support for namespaces (<a href="https://youtrack.jetbrains.com/issue/KT-11968/Research-and-prototype-namespace-based-solution-for-statics-and-static-extensions">yet</a>). We can supersede it by grouping the use cases under a single <span class="pre-inline">sealed interface</span> for this purpose. You can use <span class="pre-inline">sealed class</span> or <span class="pre-inline">object</span> as an alternative, but the memory footprint would be slightly bigger.</p><pre><code class="kotlin hljs">sealed interface PurchaseOrderUseCase { class Submit { … } class Cancel { … } } </code></pre><h2>Kotlin call site</h2><p>Now let's look at the call site:</p><pre><code class="kotlin hljs">val purchaseOrder = PurchaseOrder(...) val submitPurchaseOrderUseCase = PurchaseOrderUseCase.Submit() val result = submitPurchaseOrderUseCase.execute(purchaseOrder) </code></pre><p>We can polish it with a little trick called Invoke operator. When a Kotlin function named <span class="pre-inline">invoke()</span> is marked as <span class="pre-inline">operator</span>, its invocation may be replaced by the use of <span class="pre-inline">()</span> operator.</p><p>When applied to our base type</p><pre><code class="kotlin hljs">interface UseCase<in Input: Any, out Output: Any> { operator fun invoke(input: Input): Output } </code></pre><p>the call site can be simplified to</p><pre><code class="kotlin hljs">val purchaseOrder = PurchaseOrder(...) val submitPurchaseOrder = PurchaseOrderUseCase.Submit() val result = submitPurchaseOrder(purchaseOrder) </code></pre><p>Notice the use case instance name change, where the <span class="pre-inline">UseCase</span> suffix has been omitted, and its invocation now looks like a standard function call. If it is used in an obvious context, we may shorten the use case instance variable name even more and call it simply <span class="pre-inline">submit(purchaseOrder)</span>.</p><h2>Swift call site</h2><p>Until now, we were only calling our use cases from Kotlin code. On Kotlin Multiplatform projects, it is common to share the domain logic between all targeted platforms. Let's check how a use case may be used in another language. We will take the example of a mobile application implemented both for Android in Kotlin and iOS in Swift.</p><p>First, let's try to call it the same way we do in Kotlin.</p><pre><code class="kotlin hljs">val submitPurchaseOrder = PurchaseOrderUseCase.Submit() val result: SubmitResult = submitPurchaseOrder(purchaseOrder) </code></pre><p>This code has two major problems. The invocation operator is unavailable, and the result is not of type <span class="pre-inline">SubmitResult</span>. Let's start with the latter one.</p><h3>Swift generics</h3><p>Generics in swift are available only for <span class="pre-inline">class</span>, <span class="pre-inline">struct</span> and <span class="pre-inline">enum</span>. A very similar thing for <span class="pre-inline">interface</span> is called associated types, but its characteristic is incompatible with Kotlin generics.</p><p>This is why Kotlin Multiplatform has Swift generics support only for classes. To fix this in our project we need to define our base type as an <span class="pre-inline">abstract class</span> instead of an <span class="pre-inline">interface</span>. This is actually not an issue as its implementations are classes anyway:</p><pre><code class="kotlin hljs">abstract class UseCase<in Input: Any, out Output: Any> { abstract operator fun invoke(input: Input): Output } </code></pre><h3>Swift function invocation</h3><p>Now let's fix the former problem with use case invocation. Swift language doesn't have an invoke operator like Kotlin. Instead, it has methods with special names, and one of them is <span class="pre-inline">callAsFunction()</span> that can be called with the use of <span class="pre-inline">()</span> symbol.</p><p>We may introduce this function to our use case base type but that may be confusing for use from Kotlin common code or Android code.</p><p>To make it available for iOS code only, we will provide different implementations of use case base type for Android and iOS using Kotlin's <span class="pre-inline">expect</span>/<span class="pre-inline">actual</span> feature. Its implementation just delegates the call to our standard <span class="pre-inline">invoke()</span> function.</p><pre><code class="kotlin hljs">// Kotlin Common expect abstract class UseCase<in Input : Any, out Output : Any> constructor() { abstract operator fun invoke(input: Input): Output } // Kotlin Android actual abstract class UseCase<in Input : Any, out Output : Any> { actual abstract operator fun invoke(input: Input): Output } // Kotlin iOS actual abstract class UseCase<in Input : Any, out Output : Any> { actual abstract operator fun invoke(input: Input): Output fun callAsFunction(input: Input): Output = invoke(input) } </code></pre><h2>Living documentation</h2><p>The domain is a part of every project that actually does the important things. When modeled properly, it suits so-called living documentation. It is understandable for any reader; anytime a logic is changed, the documentation also changes. </p><p>Anytime you find a piece of domain code that is not understandable, let's take a moment and polish it. Your future will be grateful to you.</p><p><br></p><p>Pavel Švéda<br></p><p>Twitter: <a href="https://twitter.com/xsveda">@xsveda</a><br><br></p>​<br>#domain;#architecture;#kotlin;#swift;#multiplatform
Architecture tests with ArchUnit, pt. 1: Reasonshttps://mobileit.cz/Blog/Pages/arch-unit-1.aspxArchitecture tests with ArchUnit, pt. 1: Reasons<p>Good architecture is essential for a codebase to enjoy a long and happy life (the other crucial ingredient is running automated unit tests, but that’s a different blog post). Nowadays there are many sensible options, including our favorite for mobile apps, Clean arch, but no matter which one you choose, you’ll need to document and enforce it somehow.</p><p>The former is traditionally accomplished by some form of oral history passed from one developer to another (sometimes augmented by blurry photos of frantically scribbled whiteboard diagrams), while the latter is sporadically checked during code reviews (if there’s time—which there isn’t—and you remember all the rules yourself—which you don’t).</p><p>Or maybe you even have a set of gradually outdated wiki pages and fancy UML models created in some expensive enterprise tool. Or complicated but incomplete rules written for rather arcane static analysis frameworks. Or any other form of checks and docs, which are usually hard to change, detached from the actual code and difficult and rather expensive to maintain.</p><p>But fret not! There is a new architectural sheriff in this JVM town of ours and he’s going to take care of all of this—say hello to your new best friend, <a href="https://www.archunit.org/">ArchUnit</a>! </p><h2>What’s all the fuss about?</h2><p>ArchUnit is a library to, well, unit test your architecture. There are other tools to <em>check</em> your architecture, but the “unit testing” part of ArchUnit is actually its killer feature.</p><p>While “normal” unit tests should describe behavior (not structure!) of the system under test, ArchUnit cleverly leverages JVM and existing unit test frameworks to let you document <em>and</em> check your architecture in a form of runnable unit tests, executable in your current unit test environment (because you already have a strong suite of unit tests, right?). Why exactly is this such a welcome improvement?</p><p>Well, it all boils down to the fundamental benefits of all unit tests: Because unit tests are code, they are a precise, up-to-date, unambiguous, executable specification of the system. Docs can be outdated and misleading, but unit tests either compile or don’t; they either pass or not. Imagine opening a project you don’t know anything about, running its unit tests and seeing this:</p> <img alt="ArchUnit test results in Android Studio" src="/Blog/PublishingImages/Articles/arch-unit-1-01.png" data-themekey="#" /> <p>Suddenly the whole onboarding situation looks much brighter, doesn’t it?</p><h2>Show me the code</h2><p>Enough talk, let’s get down to business! If your test framework of choice is JUnit 4, put this in your <span class="pre-inline">build.gradle.kts</span>:</p><pre><code class="kotlin hljs">dependencies { testImplementation("com.tngtech.archunit:archunit-junit4:0.14.1") } </code></pre><p>There are artifacts for other test frameworks as well, just refer to the <a href="https://www.archunit.org/userguide/html/000_Index.html#_installation">docs</a>. Be careful not to use older versions as this version contains important fixes for multi-module projects containing Android libraries in a CI environment.</p><p>Now we can write our first architecture test:</p><pre><code class="kotlin hljs">@RunWith(ArchUnitRunner::class) @AnalyzeClasses(packages = ["com.example.myapp"]) internal class UiLayerTest { @ArchTest val `view model subclasses should have correct name` = classes().that().areAssignableTo(ViewModel::class.java) .should().haveSimpleNameEndingWith("ViewModel") } </code></pre><p>And just like that, you now have one small naming convention documented and automatically verified across your whole project. The API does a great job at being self-explanatory and we’ll get into the details later, but let’s quickly recap what we have here:</p><p><span class="pre-inline">@AnalyzeClasses</span> annotation is one of the ways to specify what to check. Here, we simply want to test all code in the <span class="pre-inline">com.example.myapp</span> package and its subpackages. ArchUnit imports and checks Java bytecode (not source files), which is why it works with Kotlin (or any other JVM language), although it’s itself a pure Java library—another example of Kotlin’s stellar interoperability with Java. <em>Where</em> ArchUnit actually gets this bytecode is a slightly more complicated question, but that’s not important right now.</p><p>Anyway, we annotate our test cases with <span class="pre-inline">@ArchTest</span> and for the shortest syntax, we use properties instead of functions. As with other unit tests, it’s a good idea to leverage Kotlin’s escaped property names for more readable test outputs.</p><p>And then finally for the main course: ArchUnit has a comprehensive, very expressive and really rather beautiful fluent API for specifying the predicates and their expected outcomes. It’s not Java reflection and being a pure Java library, ArchUnit doesn’t have constructs for Kotlin-exclusive language elements, but it’s still more than powerful enough.</p><h2>Test the tests</h2><p>Now run the test. Most projects probably stick to this naming convention, so the result bar in your favorite IDE might be green already. But wait! How do we know that the tests actually work?</p><p>Although they may appear a bit strange, ArchUnit tests are still unit tests and we should treat them as such. That means we should follow the famous red-green-refactor cycle, albeit modified, because you absolutely need to see the test fail and it must fail for the correct reason. This is the only time when you actually test your tests!</p><p>What does this mean for ArchUnit tests? The difference from normal TDD for our specific test case is that we cannot simply write the test first and watch it fail, because if there are no view models in the project yet, the test will pass. So we need to cheat a little and break the architecture on purpose, manually, by creating a temporary class violating the naming convention in the main source set. Then we run the test, watch it fail, delete the class and watch the test go green (the refactoring part isn’t really applicable here).</p><p>This looks like extra work and granted, it can be a bit tedious, but the red part of the cycle simply cannot be skipped, ever. There is a myriad of logical and technical errors that can result in the test being wrong or not executed at all and this is your only chance to catch them. There’s nothing worse than a dead lump of code giving you a false sense of security.</p><p>And there’s one more thing to borrow from TDD playbook: Perhaps you are doing a code review or approving pull request and you discover some construction violating a rule that you haven’t thought of before. What to do with that? As with all new bugs, don’t rush fixing the bug! The first thing you should do is write a test exposing the bug (the red part of the cycle)—that means writing an ArchUnit rule which will fail with the offending code. Only after that, make the test green. This way, you’ll slowly make your test suite more precise, with the added bonus that future regressions will be prevented as well.</p><h2>Be careful what you test for</h2><p>We’ll take a look at all ArchUnit’s fluent API constructs in a future post, but there’s an important detail we need to discuss before that.</p><p>Basically all simple ArchUnit rules follow the form <span class="pre-inline">(no) LANGUAGE_ELEMENT that PREDICATE should (not) CONDITION</span>. From a mathematical point of view, these rules are <em>implications</em>.</p><p>An implication looks like this:</p> <img alt="Venn diagram of an implication" src="/Blog/PublishingImages/Articles/arch-unit-1-02.png" data-themekey="#" /> <p>For our example test above (and many other tests that you’ll write), it means that the test will pass for <em>all</em> these variants:</p><ul><li>class is not assignable to <span class="pre-inline">ViewModel::class</span> and does not have a simple name ending with <span class="pre-inline">ViewModel</span> (that’s OK) </li><li>class is assignable to <span class="pre-inline">ViewModel::class</span> and has a simple name ending with <span class="pre-inline">ViewModel</span> (that’s also OK) </li><li>class is not assignable to <span class="pre-inline">ViewModel::class</span> and has a simple name ending with <span class="pre-inline">ViewModel</span> (the criss-crossed part of the diagram; we don’t really want to allow this) </li></ul><p>It seems that what we really want is an equivalence:</p> <img alt="Venn diagram of an equivalence" src="/Blog/PublishingImages/Articles/arch-unit-1-03.png" data-themekey="#" /> <p>Although ArchUnit doesn’t (yet?) have API elements to specify equivalences, they are fairly simple to create: Because A ↔ B is the same as (A → B) AND (B → A), we just need to add another test to our suite:</p><pre><code class="kotlin hljs">@ArchTest val `classes named ViewModel should have correct super class` = classes().that().haveSimpleNameEndingWith("ViewModel") .should().beAssignableTo(ViewModel::class.java) </code></pre><p>This way, the offending case which the first test didn’t catch (class name ends with <span class="pre-inline">ViewModel</span>, but it is not assignable to <span class="pre-inline">ViewModel.java</span>) is prevented.</p><h2>Best thing since sliced bread</h2><p>I don’t want to use the word game-changer, but I just did. Since we started adding ArchUnit tests to our projects, we have seen significant improvements in developer productivity and the health of our codebases. Compared to similar solutions, ArchUnit’s simple integration, ease of use and expressive powers are unmatched.</p><p>We’ve only scratched the surface of what’s possible, so <a href="/Blog/Pages/arch-unit-2.aspx">next time</a>, we’ll dive into ArchUnit APIs to discover some nifty architecture testing goodness! </p> #architecture;#jvm;#tdd;#android
Architecture tests with ArchUnit, pt. 2: Ruleshttps://mobileit.cz/Blog/Pages/arch-unit-2.aspxArchitecture tests with ArchUnit, pt. 2: Rules<p>In the <a href="/Blog/Pages/arch-unit-1.aspx">first part</a> of this series, we’ve had a glimpse of an architecture test written with the almighty ArchUnit, but of course there’s much more! Although ArchUnit’s API looks like Java Reflection API, it also contains powerful constructs to describe dependencies between code or predefined rules for testing popular architecture styles. Let’s see what we’ve got to play with!</p><h2>First things first</h2><p>ArchUnit rules follow the pattern <span class="pre-inline">(no) LANGUAGE_ELEMENT that PREDICATE should (not) CONDITION</span>. So what language elements can we use?</p><p>All tests begin with static methods in <span class="pre-inline">ArchRuleDefinition</span> class (but please import the class to make the rules more readable).</p><p>We can start with <span class="pre-inline">classes</span> or <span class="pre-inline">constructors</span> which are pretty self-explanatory. We also have <span class="pre-inline">theClass</span> if you want to be brief and specific. If possible, always use the overload that takes <span class="pre-inline">Class<*></span> argument instead of the overload that takes String to make your tests resistant to future refactorings; the same goes for other methods with these argument choices.</p><p>Next, we have <span class="pre-inline">fields</span>, <span class="pre-inline">methods</span> and <span class="pre-inline">members</span>. When testing Kotlin code, be extra careful with <span class="pre-inline">fields</span> because Kotlin properties are <em>not</em> Java fields. Remember that ArchUnit checks compiled bytecode and every Kotlin property is actually compiled to getter method by prepending the <span class="pre-inline">get</span> prefix, setter method by prepending the <span class="pre-inline">set</span> prefix (only for <span class="pre-inline">var</span> properties) and private field with the same name as the property name, but <em>only for properties with backing fields</em>. When testing Kotlin properties, it may sometimes be safer to test their generated getters or setters. Anyway, these subtle details show the importance of watching your test fail.</p><p>We also have a slightly mysterious <span class="pre-inline">codeUnits</span> method—it means simply anything that can access other code (including methods, constructors, initializer blocks, static field assignments etc.).</p><p>All methods mentioned above also have their negated variants. Now what can we do with all this?</p><h2>Packages, packages everywhere</h2><p>Consistent packaging is one of the most important things to get right in the project. We strongly prefer packaging by features first, then by layers. This concept sometimes goes by the name of “screaming architecture”: For example, when you open an Android project and you see top level packages such as <span class="pre-inline">map</span>, <span class="pre-inline">plannedtrips</span>, <span class="pre-inline">routeplanning</span>, <span class="pre-inline">speedlimits</span>, <span class="pre-inline">tolls</span>, <span class="pre-inline">vehicles</span> or <span class="pre-inline">voiceguidance</span>, you’ll get a pretty good idea about what the app is really about. But if instead you are looking at packages such as <span class="pre-inline">activities</span>, <span class="pre-inline">fragments</span>, <span class="pre-inline">services</span>, <span class="pre-inline">di</span>, <span class="pre-inline">data</span>, <span class="pre-inline">apis</span>, etc., it won’t tell you much about the application (every Android app will contain at least some of those things).</p><p>ArchUnit can enforce correct package structure, prevent deadly cyclic dependencies and much more. Let’s see a few examples (the actual packages mentioned are not important, use what is convenient for your project):</p><pre><code class="kotlin hljs">@ArchTest val `every class should reside in one of the specified packages` = classes().should().resideInAnyPackage( "..di", "..ui", "..presentation", "..domain", "..data" ) </code></pre><p>The two dots mean “any number of packages including zero”, so this test says that every class must exist in one of these predefined leaf packages.</p><p>This test however doesn’t say anything about the package structure <em>above</em> the leaves, so if you want to be more strict, you can write this, for example: </p><pre><code class="kotlin hljs">@ArchTest val `every class should reside in one of the specified packages` = classes().should().resideInAnyPackage( "com.example.myapp.*.di", "com.example.myapp.*.ui", "com.example.myapp.*.presentation", "com.example.myapp.*.domain", "com.example.myapp.*.data" ) </code></pre><p>The star matches any sequence of characters excluding the dot (for our sample packaging, in its place there would be a feature name), but you can also use <span class="pre-inline">**</span> which matches any sequence of characters <em>including</em> the dot. Together with the two dot notation, you can express pretty much any package structure conceivable (see the Javadoc for <span class="pre-inline">PackageMatcher</span> class).</p><h2>Building the walls</h2><p>One popular architectural style is to divide the code into layers with different levels. We can define layer level simply as the code’s distance from inputs/outputs—so things like UI, DB or REST clients are pretty low-level, whereas business logic and models are on the opposite side and the application logic sits somewhere in the middle.</p><p>In this case, it’s a good idea to isolate higher-level layers from external dependencies such as platform SDK or other invasive frameworks and libraries, since higher levels should be more stable and independent of the implementation details in lower layers. ArchUnit can help us with that:</p><pre><code class="kotlin hljs">@ArchTest val `higher-level classes should not depend on the framework` = noClasses().that().resideInAnyPackage("..presentation..", "..domain..") .should().dependOnClassesThat().resideInAnyPackage( "android..", "androidx..", "com.google.android..", "com.google.firebase.." /* and more */ ) </code></pre><p>Only a few lines and those pesky imports have no way of creeping in your pristine (and now even fairly platform-independent!) code.</p><h2>Piece(s) of cake</h2><p>Speaking of layers, we should not only handle their dependencies on the 3rd party code, but of course also the direct dependencies between them. Although we can use the constructs mentioned above, ArchUnit has another trick up to its sleeve when it comes to layered architectures.</p><p>Suppose we have defined these layers and their <em>code</em> dependencies:</p> <img alt="Example layer structure" src="/Blog/PublishingImages/Articles/arch-unit-2-01.png" data-themekey="#" /> <p>This is just an example, but let’s say that the domain layer is the most high-level, so it must not depend on anything else; presentation and data layers can depend on stuff from domain, UI can see view models in presentation layer (but view models must not know anything about UI) and DI sees all to be able to inject anything (and ideally, no other layer should see DI layer, because classes should not know anything about how they are injected; alas this is not always technically possible).</p><p>Whatever your actual layers are, the most important thing is that all dependencies go in one direction only, from lower level layers to higher level layers (this is the basic idea of Clean architecture). ArchUnit can encode these rules in one succinct test:</p><pre><code class="kotlin hljs">@ArchTest val `layers should have correct dependencies between them` = layeredArchitecture().withOptionalLayers(true) .layer(DOMAIN).definedBy("..domain") .layer(PRESENTATION).definedBy("..presentation") .layer(UI).definedBy("..ui") .layer(DATA).definedBy("..data") .layer(DI).definedBy("..di") .whereLayer(DOMAIN).mayOnlyBeAccessedByLayers(DI, PRESENTATION, DATA) .whereLayer(PRESENTATION).mayOnlyBeAccessedByLayers(DI, UI) .whereLayer(UI).mayOnlyBeAccessedByLayers(DI) .whereLayer(DATA).mayOnlyBeAccessedByLayers(DI) .whereLayer(DI).mayNotBeAccessedByAnyLayer() </code></pre><p>How does it work? <span class="pre-inline">layeredArchitecture()</span> is a static method in the <span class="pre-inline">Architectures</span> class (again, please import it). First we need to actually define our layers: <span class="pre-inline">layer</span> declares the layer (the argument is simply any descriptive String constant) and <span class="pre-inline">definedBy</span> specifies a package by which the layer is, well, defined (you can use package notation which we’ve seen before; you can also use a more general predicate). Without <span class="pre-inline">withOptionalLayers(true)</span> call, ArchUnit will require that all layers exist, which in a multi-module project might not necessarily be true (some modules might for example contain only domain stuff).</p><p>This rather short test will have an enormous impact on your codebase—correctly managed dependencies are what prevents your project from becoming a giant mess of spaghetti code.</p><h2>Inner beauty</h2><p>We’ve sorted the layers and packages, but what about their content? Take for example the domain layer: Continuing our rather simplified example, we want only <span class="pre-inline">UseCase</span> classes and <span class="pre-inline">Repository</span> interfaces in there. Furthermore, we want for these classes to follow certain name conventions and to extend correct base classes.</p><p>We can express all these requirements by the following set of ArchUnit tests:</p><pre><code class="kotlin hljs">@ArchTest val `domain layer should contain only specified classes` = classes().that().resideInAPackage("..domain..") .should().haveSimpleNameEndingWith("UseCase") .andShould().beTopLevelClasses() .orShould().haveSimpleNameEndingWith("Repository") .andShould().beInterfaces() @ArchTest val `classes named UseCase should extend correct base class` = classes().that().haveSimpleNameEndingWith("UseCase") .should().beAssignableTo(UseCase::class.java) @ArchTest val `use case subclasses should have correct name` = classes().that().areAssignableTo(UseCase::class.java) .should().haveSimpleNameEndingWith("UseCase") </code></pre><p>And as a bonus example for Android fans, you can, of course, be even more specific:</p><pre><code class="kotlin hljs">@ArchTest val `no one should ever name fields like this anymore ;)` = noFields().should().haveNameMatching("m[A-Z]+.*") </code></pre><h2>Endless power</h2><p>We’ve seen only a smart part of the ArchUnit API, but there’s almost nothing that ArchUnit tests cannot handle. You can examine all Java constructs and their wildest combinations (but always be aware of Kotlin-Java interoperability details and test your tests), go explore!</p><p>Next time, we’ll take a look at some advanced features and configuration options.</p>#architecture;#jvm;#tdd;#android
Architecture tests with ArchUnit, pt. 3: Advanced stuffhttps://mobileit.cz/Blog/Pages/arch-unit-3.aspxArchitecture tests with ArchUnit, pt. 3: Advanced stuff<p>​​​ArchUnit has many tricks up to its sleeve. We’ve <a href="/Blog/Pages/arch-unit-2.aspx">already seen</a> how to check package structure, language elements such as classes, fields, and methods, and how to make sure your layered architecture is sound. But there’s more! Let’s take a look at some advanced conce​pts.</p><h2>Slicing and dicing</h2><p>As we’ve seen in the previous part of this series, ArchUnit makes it easy to test layers and their relationships. However, dividing your codebase only horizontally isn’t enough—in all but very tiny projects, you’d end up with huge layers containing too many unrelated classes. Thus we need to divide our code vertically as well, usually by user-facing features and project-specific or general-purpose libraries. Those features or libraries are often compilation units (e.g., Gradle modules), but that doesn’t need to concern us at this moment.</p> <img alt="Package and module structure" src="/Blog/PublishingImages/Articles/arch-unit-3-01.png" data-themekey="#" /> <p>So, if we have defined several vertical slices of our codebase, we would like to test their relationships as well. Horizontal layer tests work <em>across</em> all slices, so they won’t help us in this case, but ArchUnit has us covered with its high-level slices API:</p><pre><code class="kotlin hljs">@ArchTest val `feature slices should not depend on each other` = slices().matching("com.example.myapp.feature.(*)..") .should().notDependOnEachOther() </code></pre><p>This is a good rule to have, as you usually want your features to be isolated from each other. How does it work?</p><p>First, we define the matcher which slices the codebase vertically: It takes package notation which we’ve seen in the previous rules. The matcher group denoted by parentheses specifies the actual slicing point as well as the slice’s name shown in error messages.</p><p>In this case, code units residing in the following example packages <em>and</em> their subpackages would constitute separate slices: <span class="pre-inline">com.example.myapp.feature.login</span>, <span class="pre-inline">com.example.myapp.feature.map</span> or <span class="pre-inline">com.example.myapp.feature.navigation</span>. </p> <img alt="Feature slices" src="/Blog/PublishingImages/Articles/arch-unit-3-02.png" data-themekey="#" /> <p>In contrast, writing <span class="pre-inline">matching("com.example.myapp.feature.(**)")</span>, then <span class="pre-inline">com.example.myapp.feature.login.model</span> and <span class="pre-inline">com.example.myapp.feature.login.data</span> would constitute <em>different</em> slices.</p> <img alt="Layer slices" src="/Blog/PublishingImages/Articles/arch-unit-3-03.png" data-themekey="#" /> <p>Be careful, as this distinction might be rather subtle!</p><p>As always when practicing TDD, you need to see your test fail for the right reason—in this case that means creating a temporary file that intentionally breaks the test and deleting it afterwards.</p><p>The rest of the rule is simple: After the usual <span class="pre-inline">should()</span> operator, we have only two options: <span class="pre-inline">notDependOnEachOther()</span> tests that, well, no slice depends on any other (unlike the layer dependency tests, these tests are bi-directional), whereas <span class="pre-inline">beFreeOfCycles()</span> allows dependencies between the slices, but only in one direction at most.</p><p>Generally speaking, it may be a good idea to run the <span class="pre-inline">beFreeOfCycles()</span> test on every slice (using one of the two test variants mentioned above) in your codebase, whereas some types of slices (typically libraries, but not features) may be permitted to depend on each other in one direction. </p><p>But what if your codebase isn’t structured in such a convenient way? For example, there might be no middle <span class="pre-inline">feature</span> package distinguishing features from libraries, or worse, the package structure may be completely inconsistent.</p><p>For such cases, ArchUnit contains handy <span class="pre-inline">SliceAssignment</span> interface which you can use to assign slices to classes in a completely arbitrary way:</p><pre><code class="kotlin hljs">@ArchTest private val features = object : SliceAssignment { override fun getIdentifierOf(javaClass: JavaClass) = when { javaClass.packageName.startsWith("com.example.myapp.login") -> SliceIdentifier.of("feature-login") javaClass.name.contains("map") -> SliceIdentifier.of("feature-map") /* ... whatever you need ... */ else -> SliceIdentifier.ignore() override fun getDescription() = "this will be added to the error message" } </code></pre><p>Strings given to <span class="pre-inline">SliceIdentifier</span> are arbitrary constants identifying the slice and are also shown in error messages.</p><p>There is an important difference in what you write in that <span class="pre-inline">else</span> branch: If you return <span class="pre-inline">SliceIdentifier.of("remaining")</span>, then all classes not matching the previous cases will be assigned to the <span class="pre-inline">"remaining"</span> slice (which means they will be tested against other slices), whereas if you return <span class="pre-inline">SliceIdentifier.ignore()</span>, those classes won’t participate in the test at all (both options have their uses, but be careful not to confuse them).</p><p>We can then use our slice assignment like this:</p><pre><code class="kotlin hljs">slices().assignedFrom(features).should().notDependOnEachOther()</code></pre><h2>Why be in when you could be out?</h2><p>As we’ve learned, ArchUnit runs its tests on compiled bytecode. But where do these classes come from?</p><p>There is more than one way to specify that, but probably the most succinct is to use this annotation:</p><pre><code class="kotlin hljs">@RunWith(ArchUnitRunner::class) @AnalyzeClasses(packages = ["com.example.myapp"]) internal class MyArchTest </code></pre><p>Besides using String literals, we can specify packages with Classes or, if that’s not enough, completely customize the sources using ArchUnit’s <span class="pre-inline">LocationProvider</span>. In every case, please note that ArchUnit looks for packages within the current classpath and <em>all</em> classes must be imported for ArchUnit to be able to work correctly—if you import class <span class="pre-inline">X</span>, you need to import all its dependencies as well, transitively, otherwise ArchUnit will have only a limited amount of information to work with and the tests might yield false positives, giving you a false sense of security.</p><p>Now we have all the class locations that ArchUnit needs, but there are situations when we don’t necessarily need to test against all of the classes in there. We can filter the classes with <span class="pre-inline">importOptions</span>:</p><pre><code class="kotlin hljs">@RunWith(ArchUnitRunner::class) @AnalyzeClasses( packages = ["com.example.myapp"], importOptions = [ DoNotIncludeArchives::class, DoNotIncludeTests::class, DoNotIncludeAndroidGeneratedClasses::class ] ) internal class MyArchTest </code></pre><p>ArchUnit comes with a couple of handy predefined import options, such as the first two, or we can write our own, which is simple enough:</p><pre><code class="kotlin hljs">internal class DoNotIncludeAndroidGeneratedFiles : ImportOption { companion object { private val pattern = Pattern.compile(".*/BuildConfig\\.class|.*/R(\\\$.*)?\\.class|.*Binding\\.class") } override fun includes(location: Location) = location.matches(pattern) } </code></pre><p>This import option rejects Android <span class="pre-inline">BuildConfig</span>, <span class="pre-inline">R</span>, and <span class="pre-inline">Binding</span> classes. The location argument passed here is platform-independent, so you don’t have to worry about path separators and such things.</p><p>But what if we need to be more granular? For example, sometimes we might need to ignore certain classes on a per-test basis, basically adding ad-hoc exceptions to our pristine rules, because, you know, the real world happens. This is a slippery slope, so don’t forget to document such situations, but it’s simple enough to do by adding <span class="pre-inline">or</span> clause to the rule:</p><pre><code class="kotlin hljs">@ArchTest val `domain layer should contain only specified classes` = classes().that().resideInAPackage("..domain..") .should().haveSimpleNameEndingWith("Repository").andShould().beInterfaces() .orShould().haveSimpleNameEndingWith("Controller").andShould().beInterfaces() .orShould().haveSimpleNameEndingWith("UseCase") .orShould().be(Data::class.java) .because("only repositories, controllers and use cases are permitted in domain and Data is special wrapper for results from those classes") </code></pre><p>Be specific as possible with your exceptions as you don’t want them to accidentally match more language elements than intended—here, using class literal instead of String is safe and future refactoring-proof.</p><p><span class="pre-inline">Because</span> clause allows you to add more detail to the default error message generated from the test case name. There is also <span class="pre-inline">`as`</span> clause (ArchUnit being written in Java, it accidentally overloads Kotlin’s keyword, so don’t forget to escape it or create an alias extension function with a Kotlin-friendly name) that allows you to completely override the error message.</p><h2>Godspeed</h2><p>Because ArchUnit does a lot under the hood (bytecode examination and cycle checks tend to be expensive, among other things), the tests themselves, although written using unit test infrastructure, seldom have the <em>speed</em> of normal unit tests. The speed penalty can be quite severe, so organize your test suites in a way that ArchUnit tests don’t slow you down when developing your code using the classic TDD cycle of red-green-refactor, which has to be always blazingly fast.</p><p>That said, all ArchUnit’s features working together allow us to express our architecture in a clean, succinct, and <em>executable</em> way, which is invaluable in larger projects. Consistency and clarity are virtues that may very well make the difference between a codebase that is easy to learn and maintain and one that becomes a big steaming pile of unreadable mess.</p>#architecture;#jvm;#tdd;#android
Architecture tests with ArchUnit, pt. 4: Extensions & Kotlin supporthttps://mobileit.cz/Blog/Pages/arch-unit-4.aspxArchitecture tests with ArchUnit, pt. 4: Extensions & Kotlin support<p>​ArchUnit is <a href="/Blog/Pages/arch-unit-3.aspx">immensely capable on its own</a> and that's a great merit on its own, but it doesn’t stop there—ArchUnit’s power can be augmented by adding custom matchers, language elements, and even whole new concepts. In this post, we’ll look at how we can achieve that and then we’ll see if we can leverage these capabilities to support even Kotlin-exclusive language elements in ArchUnit tests (spoiler alert: yes, we can!). Ready?</p><h2>Shiny new things</h2><p>As we’ve mentioned several times, ArchUnit rules look like this:</p><pre><code class="kotlin hljs">(no) LANGUAGE_ELEMENT that PREDICATE should (not) CONDITION</code></pre><p>As it turns out, by thoroughly following the open/closed principle, ArchUnit allows us to supply our own language elements, predicates and conditions. These can be simple aggregations of existing built-in predicates or conditions to facilitate reuse, or we can invent entirely new domain-specific concepts to utilize in our architecture tests. So how is it done?</p><p>To create a custom language element, predicate or condition, we need to extend <span class="pre-inline">AbstractClassesTransformer</span>, <span class="pre-inline">DescribedPredicate</span>, or <span class="pre-inline">ArchCondition</span> respectively. Each abstract base class takes one type argument—the language element it operates on (ArchUnit provides for example <span class="pre-inline">JavaClass</span>, <span class="pre-inline">JavaMember</span>, <span class="pre-inline">JavaField</span> or <span class="pre-inline">JavaCodeUnit</span> and we can even create our own; these are reflection-like models read from compiled bytecode). They also have one constructor argument, a String that is appended to the rule description in error messages.</p><p>The simplest one to implement is <span class="pre-inline">DescribedPredicate</span>—we need to override its <span class="pre-inline">apply</span> method which takes the examined language element and returns Boolean:</p><pre><code class="kotlin hljs">val myPredicate = object : DescribedPredicate("rule description") { override fun apply(input: JavaClass): Boolean = // … }</code></pre><p><span class="pre-inline">ArchCondition</span> is slightly more involved, as its <span class="pre-inline">check</span> function takes the language element as well. In addition, it also takes <span class="pre-inline">ConditionEvents</span> collection, which is used to return the result of the evaluation, as this function doesn’t directly return anything: </p><pre><code class="kotlin hljs">val myCondition = object : ArchCondition("condition description") { override fun check(item: JavaClass, events: ConditionEvents) { if (item.doesNotSatisfyMyCondition()) { events.add(SimpleConditionEvent.violated(item, "violation description")) } } }</code></pre><p><span class="pre-inline">AbstractClassesTransformer</span> has a <span class="pre-inline">doTransform</span> method which takes a collection of <span class="pre-inline">JavaClass</span>es and transforms it to another collection. Elements of the output collection can be <span class="pre-inline">JavaClass</span>es as well, different built-in types or even custom classes. The transformation may comprise any number of operations including mapping or filtering:</p><pre><code class="kotlin hljs">val myTransformer = object : AbstractClassesTransformer("items description") { override fun doTransform(collection: JavaClasses): Iterable = collection.filter { /* ... */ }.map { /* ... */ } }</code></pre><p>Anyway, we can use our custom transformers, predicates, and conditions like this:</p><pre><code class="kotlin hljs">all(encryptedRepositories) .that(handleGdprData) .should(useSufficientlyStrongCrypthographicAlgorithms)</code></pre><p>and they can, of course, be combined with the built-in ones.</p><p>Besides promoting reuse, custom transformers, predicates, and conditions are particularly good at increasing the level of abstraction of your tests—it’s better to describe your system using its domain language instead of low-level, opaque technical terms.</p><h2>Gimme some Kotlin lovin’</h2><p>As promised, now it’s the time to tackle the last thing we’d like to have in ArchUnit tests—Kotlin support.</p><p>Because ArchUnit reads compiled bytecode and Kotlin has killer Java interoperability, we can get pretty far out of the box, but we still can’t directly test for Kotlin stuff like sealed and data classes, objects, typealiases, suspending functions etc. To find out how that could be possible, we need to take a slight detour first.</p><p>When targeting JVM (or Android), Kotlin compiler outputs JVM bytecode. Adding custom bytecodes for Kotlin-exclusive constructs is of course out of the question, so the compiler must resort to clever tricks to convert Kotlin stuff to vanilla JVM bytecode. Now, there’s some wiggle room (JVM bytecode allows some things that Java as a language doesn’t), but still, to achieve Kotlin’s stellar level of Java interoperability, the compiler must mostly play by Java’s rules.</p><p>To achieve that, for example, Kotlin compiler generates getters, setters and backing fields for properties. It also creates encapsulating classes for top level functions and properties, adds new functions to existing classes (to support data classes) or adds parameters to existing functions (this is one of the tricks behind suspending functions).</p><p>As a result, when examining the bytecode alone, those Kotlin concepts effectively disappear. But for Kotlin compiler to be able to compile Kotlin code against another <em>already compiled</em> Kotlin code (and to see it as Kotlin code, not generic JVM code), this information must be preserved somewhere.</p><p>Take for example this simple data class:</p><pre><code class="kotlin hljs">data class Money(val amount: BigDecimal, val currency: Currency)</code></pre><p>IntelliJ Idea/Android Studio lets us see bytecode generated from this Kotlin code, which in turn can be (in most cases) decompiled to equivalent Java code. If we do that with the <span class="pre-inline">Money</span> class, we’ll see something like this:</p><pre><code class="java hljs">@Metadata( mv = {1, 1, 16}, bv = {1, 0, 3}, k = 1, d1 = {"\u0000,\n\u0002\u0018\u0002\n\ /* rest omitted */ }, d2 = {"Lcom/example/myapp/Money;", "", "amount", "Ljava/math/BigDecimal;", "currency", "Ljava/util/Currency;", "(Ljava/math/BigDecimal;Ljava/util/Currency;)V", "getAmount", "()Ljava/math/BigDecimal;", "getCurrency", "()Ljava/util/Currency;", "component1", "component2", "copy", "equals", "", "other", "hashCode", "", "toString", "", "app-main"} ) public final class Money { @NotNull private final BigDecimal amount; @NotNull private final Currency currency; @NotNull public final BigDecimal getAmount() { return this.amount; } @NotNull public final Currency getCurrency() { return this.currency; } /* rest omitted */</code></pre><p>Bingo! The Java part is pretty straightforward, but it looks like that strange <span class="pre-inline">@Metadata</span> stuff might be what we need. Indeed, the documentation for <span class="pre-inline">@Metadata</span> says that “This annotation is present on any class file produced by the Kotlin compiler and is read by the compiler and reflection.” Its arguments contain various interesting Kotlin-exclusive bits and pieces related to the class and because it has runtime retention, it will be stored in binary files, which means we can read them from our ArchUnit tests! If only we could make sense of that gibberish inside the annotation…</p><h2>Metadata dissection</h2><p>It turns out that we can! There’s a small official library to do just that.</p><p>First, add JVM metadata library to your build script:</p><pre><code class="kotlin hljs">dependencies { testImplementation("org.jetbrains.kotlinx:kotlinx-metadata-jvm:0.1.0") }</code></pre><p>Then, our plan of attack is this:</p><ol><li>The starting point is the input of our custom transformer, predicate, or condition, which in this case will be ArchUnit’s <span class="pre-inline">JavaClass</span> object. </li><li>ArchUnit can read annotations on the <span class="pre-inline">JavaClass</span> object, so we examine if Kotlin’s <span class="pre-inline">@Metadata</span> annotation is present. </li><li>If it is, we use the <a href="https://github.com/JetBrains/kotlin/blob/master/libraries/kotlinx-metadata/jvm/ReadMe.md">kotlinx-metadata</a> library to read the actual metadata. (KotlinPoet has a <a href="https://square.github.io/kotlinpoet/kotlinpoet_metadata/">higher-level API</a> based on kotlinx-metadata, which presumably might be a little bit nicer to use; we’ll just use the basic API here, as the end result will be the same in either case.) </li><li>We expose the data in some easily digestible object so we can write simple and readable assertions about it. </li></ol><p>To make an already long story short, here is the first piece of the puzzle—transformation from ArchUnit’s <span class="pre-inline">JavaClass</span> to kotlinx-metadata <span class="pre-inline">KmClass</span> model:</p><pre><code class="kotlin hljs">private fun JavaClass.toKmClass(): KmClass? = this .takeIf { it.isAnnotatedWith(Metadata::class.java) } ?.getAnnotationOfType(Metadata::class.java) ?.let { metadata -> KotlinClassMetadata.read( KotlinClassHeader( kind = metadata.kind, metadataVersion = metadata.metadataVersion, bytecodeVersion = metadata.bytecodeVersion, data1 = metadata.data1, data2 = metadata.data2, extraString = metadata.extraString, packageName = metadata.packageName, extraInt = metadata.extraInt ) ) } ?.let { (it as? KotlinClassMetadata.Class)?.toKmClass() }</code></pre><p>If a given <span class="pre-inline">JavaClass</span> is annotated with <span class="pre-inline">@Metadata</span>, this extension reads the annotation and converts it to a  <span class="pre-inline">KotlinClassMetadata</span> object (mapping the annotation attributes to the corresponding properties of <span class="pre-inline">KotlinClassHeader</span> along the way). </p><p><span class="pre-inline">KotlinClassMetadata</span> is a sealed class and its subclasses represent various different kinds of classes generated by the Kotlin compiler. There are a few of them, but to keep things simple we are interested only in “real” classes (<span class="pre-inline">KotlinClassMetadata.Class</span>) from which we finally extract the rich <span class="pre-inline">KmClass</span> model (and return null in all other cases).</p><p>To make our life easier later, we also add this handy extension:</p><pre><code class="kotlin hljs">private fun JavaClass.isKotlinClassAndSatisfies(predicate: (KmClass) -> Boolean): Boolean = this.toKmClass()?.let { predicate(it) } == true</code></pre><h2>Grand finale</h2><p>Now we can finally write our transformers, predicates, and conditions. Because they will be all quite similar, let’s create factory methods for them first:</p><pre><code class="kotlin hljs">fun kotlinClassesTransformer(description: String, predicate: (KmClass) -> Boolean) = object : AbstractClassesTransformer(description) { override fun doTransform(collection: JavaClasses): Iterable = collection.filter { it.isKotlinClassAndSatisfies(predicate) } }</code></pre><pre><code class="kotlin hljs">fun kotlinDescribedPredicate(description: String, predicate: (KmClass) -> Boolean) = object : DescribedPredicate(description) { override fun apply(javaClass: JavaClass) = javaClass.isKotlinClassAndSatisfies(predicate) }</code></pre><pre><code class="kotlin hljs">fun kotlinArchCondition( ruleDescription: String, violationDescription: String, predicate: (KmClass) -> Boolean ) = object : ArchCondition(ruleDescription) { override fun check(javaClass: JavaClass, events: ConditionEvents) { if (!javaClass.isKotlinClassAndSatisfies(predicate)) { events.add(SimpleConditionEvent.violated(javaClass, "$javaClass $violationDescription")) } } }</code></pre><p>And now, finally, we have everything ready to write things we can actually use in our ArchUnit tests—for example:</p><pre><code class="kotlin hljs">val kotlinSealedClasses = kotlinClassesTransformer("Kotlin sealed classes") { it.sealedSubclasses.isNotEmpty() }</code></pre><pre><code class="kotlin hljs">val areKotlinDataClasses = kotlinDescribedPredicate("are Kotlin data classes") { Flag.Class.IS_DATA(it.flags) }</code></pre><pre><code class="kotlin hljs">val beKotlinObjects = kotlinArchCondition("be Kotlin objects", "is not Kotlin object") { Flag.Class.IS_OBJECT(it.flags) }</code></pre><p>The predicate lambdas operate on <span class="pre-inline">KmClass</span> instances. <span class="pre-inline">KmClass</span> is quite a low-level but powerful API to examine <span class="pre-inline">@Metadata</span> annotation content. <span class="pre-inline">KmClass</span> has direct methods or properties for some Kotlin constructs, while others can be derived from its flags. Sometimes it takes a little bit of exploration, but all Kotlin-specific stuff is there. Or, for a higher-level API to do the same, see <a href="https://square.github.io/kotlinpoet/kotlinpoet_metadata/">KotlinPoet metadata</a>.</p><p>Now we can write tests such as:</p><pre><code class="kotlin hljs">all(kotlinSealedClasses) .that(resideInAPackage("..presentation")) .should(haveSimpleNameEndingWith("State"))</code></pre><pre><code class="kotlin hljs">classes() .that(resideInAPackage("..model")).and(areKotlinDataClasses) .should(implement(Entity::class.java))</code></pre><pre><code class="kotlin hljs">classes() .that().areTopLevelClasses().and().areAssignableTo(UseCase::class.java) .should(beKotlinObjects)</code></pre><p>So there you have it—support for Kotlin constructs in ArchUnit. The sky’s the limit, now your codebase can be more robust than ever! </p><h2>Conclusion</h2><p>Well, this was quite a journey! The benefits of having even a small suite of ArchUnit tests in your project are immense—they prevent subtle, hard-to-catch bugs, act as the best possible documentation of your architecture, save you time during code reviews and keep your codebase clean, consistent, maintainable and healthy. They are easy to write, simple to integrate into your CI/CD pipeline and extendable even beyond the original language. What’s not to like? Start writing them now and reap the rewards for years to come!</p>#architecture;#jvm;#tdd;#android;#kotlin
Development rules for better productivityhttps://mobileit.cz/Blog/Pages/development_rules_for_better_productivity.aspxDevelopment rules for better productivity<p>​Each project has its own rules for development. Most often, these rules describe development practices and work with a version control system (VCS) that outlines how to test and deploy the product, the structure, the architecture of the project, and the code style, but the rules can also include the basics of secure development and more.</p><p>These rules can be unwritten but are usually defined and recorded in a shared space where everyone has access to them. Yet more important is how these rules are applied and enforced in practice.</p><h2>Wise men know it best</h2><p>Some people believe that pair programming or code review is an appropriate platform for rules revision. Participants can agree on the application of the rules or change them based on context. When developers approach this conscientiously, the result can be a project that may not be consistent, but its individual parts will be in the best possible shape.</p><p>This approach may lead to better results for a short period of time at the start of a project, but its shortcomings will start to become apparent over time. Even experienced developers and technical leads are just people. They can occasionally get tired or simply overlook something. Thus, minor deviations from the agreed-upon rules pile up and begin to form so-called technical debt.</p><h2>Time trial</h2><p>The next aspect that reflects in time is the expansion of the project, and this is on two levels.</p><p>The first is the expansion of the product itself. As the product becomes successful, it receives new and more sophisticated features that begin to connect previously simpler and independent features. This is where the small deviations and inconsistencies that at first seemed good for the functionality start to show up. The problem is that now more of these functions need to be compatible with each other, but they are not ready for it.</p><p>This is not just a problem of incompatibility of features. Often, well-intentioned developers try to reuse existing components as much as possible to avoid code duplication. They forget the rule that we can reuse a component when we expect the same behavior but also have the same reasons to change. If this condition is not met, the Single Responsibility Principle is violated and the project has low flexibility for future development.</p><p>The second level of project expansion is the development team itself. Once the product becomes successful, sooner or later it leads to the expansion of the development team. The new team members do not know the whole genesis of the project and thus do not have the same understanding of the rules as the older members who set the rules themselves. This leads to misunderstandings and yet another minor deviation from the agreed rules, which again increases the technical debt. A side effect is an increased need for communication during pair programming and code review, which reduces the overall productivity of the team.</p><p>These problems in project expansion are not limited to large development teams, but also happen to smaller ones. For example, when a two-person team introduces two new developers, the productivity of the team may hardly change. And all this is due to the circumstances described above. Without knowing the cause of the problem, it can lead to further blind "strengthening" of the team and make it even worse.</p><h2>Tripping comes before a fall</h2><p>When a development team repeatedly fails to finish agreed features or is unable to release a new version of a product for technical reasons, one of the critical scenarios occurs. At this point, people outside the standard development team get involved in discovering the cause and solving the problem. Together, they all work under stress and with a lot of human effort to get the development back under control. It is only at this point that a retrospective analysis takes place to uncover the true causes of the problems.</p><p>Yet the accompanying symptoms and spring of these problems have been manifesting themselves for a long time in advance. A few subtle situations as examples: </p><ul><li>A developer repeatedly receives feedback on misnaming during code review.</li><li>A ticket in the project issue tracker does not contain a reference to the implementation due to a typo in the project source code. </li><li>A feature that is still under development is enabled in the test version of the application.</li></ul><p></p><p>Often they just look like minor ambiguities, individual mistakes, or accidents that sometimes happen during development. When these situations recur, their negative impact on team productivity and the project's error rate quickly increases. But if we look closely at them, we can see blank spaces in the way we work that are actually easy to fill and solve the problem once and for all.</p><h2>Live rules</h2><p>To avoid the problems mentioned above, we need to have clearly specified development rules. Their content should reflect the individual needs of each project. For some projects, it will be critical to clearly define collaboration with the QA team and describe the release management of the product, while other teams will focus more on collaboration between developers within the development itself.</p><p>The more detailed and specific the rules are, the better their effect on the team's work will be. Therefore, it is not possible to start from a general template, but each team must define these rules themselves.</p><p>Sometimes we see cases where a team defines a few basic rules of a more general nature at the start of a project but does not develop them over time. Thus, the rules never accurately represent how the team works, and even the little they contain becomes meaningless over time.</p><p>Nothing is set in stone, and even an established team changes the tools it uses or discovers new ways of working over time. All these natural changes have an impact on the team rules. They remain alive and valid.</p><h2>Automate everything</h2><p>In the introduction, we briefly discussed the form the rules can take and the possible ways of applying them. To be most effective, rules must be applied automatically without the need for manual control. Likewise, we must think about the productivity of the team, which can be reduced by the influence of a large number of repressive rules. Therefore, tools that help team members automate certain steps are an integral part of this process. These tools save time and their output is naturally in line with the defined rules.</p><p>In practice, it is so common that Code Style or Project Architecture is enforced by automated rules, but at the same time, there are code templates and formatting tools that do most of the work for the developers without them being aware of it.</p><p>Most issue tracking tools provide a programmatic interface so that it is easy to automate the creation of a new development branch in VCS so that the new code is linked to a specific issue. Similarly, it is possible to create a VCS hook that checks that a corresponding issue exists for a given branch or commit for cases when the developer has created the new development branch manually.</p><p>Release management is a completely separate chapter. When releasing a new version of a product, there are generally routine and clearly defined steps that need to be performed. Here, automation not only gives us the assurance that the release of a new version will be done in an orderly and error-free manner but it also speeds up the process and allows for easy knowledge sharing between team members. A bonus is an easy automation of generating release notes, even if only for internal testers.</p><p>Testers will also appreciate the ability to configure the product for internal testing. They can toggle a feature flag, configure the environment the application runs against, or have the ability to simulate scenarios that are difficult to do manually. Besides the undeniable benefit of these test tools for testers, it has benefits for the developers themselves. They don't have to maintain more versions of the product than strictly necessary, which saves them time by fundamentally simplifying the build logic.</p><p>This is just a small sample of the rules we commonly encounter on projects. However, it is far from exhaustive of the options, whether they are related to quantity, depth, or sophistication.</p><h2>How far to go?</h2><p>Some rules may seem strict enough to restrict your freedom to work. Others may seem so vague that they will not have the desired effect. That's all right. Every team and every member has different needs and different perceptions of risk. All of this has a bearing on the form of the rules.</p><p>In our projects, we try to define the rules strictly by default. Over time, when we find that they limit us too much, it's easy to make the rule more generalized or turn it into a recommendation or warning. The opposite approach is considerably more complicated, because the team and its operation have already become accustomed to the more general rule, so it may not be possible to make a change overnight.</p><h2>Project sustainability</h2><p>When developing a product, in addition to the short-term goals of making the necessary features, we always consider the idea of long-term sustainability and viability of the project. Especially in mobile development, it is not an exception to the opinion that a project should be discarded once every 2-3 years and developed completely from scratch. This is a real disaster in terms of productivity.</p><p>In our portfolio, we have apps that are more than 7 years old and have gone through several major changes during their lifetime. On a business level, there has been a change in targeting a different sector of users and aligned with changes in branding and design. On a technical level I would mention the switch from Java programming language to Kotlin, or the transition of asynchronous work from a custom imperative solution to a reactive approach via RxJava and later Kotlin Coroutines. Even so, the development team is still only two people (while they are altering throughout) and at the same pace, it delivers new functionalities according to the business needs.</p><p>Clearly defined development rules and the consistency enforced by them have a major impact on the quality of the final product in every way.</p><p>Next time we will take a closer look at how some rules can be implemented and what tools we have for this.</p><p><br></p><p>Pavel Švéda<br></p><p>Twitter: <a href="https://twitter.com/xsveda">@xsveda</a><br><br></p>​<br>#productivity;#team-collaboration;#architecture;#automation
Truce with fragmentshttps://mobileit.cz/Blog/Pages/fragments.aspxTruce with fragments<p>​​​Once upon a time, one could write an entire Android app in a single humongous activity. Google provided us with a bunch of fairly low-level building blocks and basically no guidance on how to use them together in a maintainable and scalable way. (“So you want to build a house? Here is a shovel, a saxophone and a kitten, we thought they might be useful, but do absolutely what you want with them, go on! And please mind the lifecycles, thank you.”)</p><p>Fortunately, things have changed a bit and now the official docs mention stuff like view models, lifecycle observers, navigation graphs, repositories or single-activity applications. And there are even official <em>opinions</em> on how to combine them!</p><p>Nevertheless, activities and fragments, the remnants of those dark times, are apparently here to stay. When you look at API surfaces of these... <em>things</em>, one question surely comes to mind: How do I work with that and stay sane at the same time?</p><h2>A match made in a place other than heaven</h2><p>Before we get to that, a little disclaimer is needed: This article is very opinionated. Your mileage and needs may vary. The following recommendations are best suited to “ordinary” form-based apps containing quite a lot of screens and user flows, with modest performance requirements. Games, specialized single-purpose apps, apps with dynamic plug-ins, high-performance, or UI-less apps might benefit from completely different approaches.</p><p>With that out of the way, the first question we should ask is: Do we even <em>need</em> activities and fragments?</p><p>With activities being the entry points to the app's UI, the answer is obviously yes. There’s no way around the fact that we need at least one activity with <span class="pre-inline">android.intent.action.MAIN</span> and <span class="pre-inline">android.intent.category.LAUNCHER</span> in its intent filter. But do we need <em>more</em> than one? The answer to that is a resounding no and we’ll see why in a future post.</p><p>Fragments are a different matter. First introduced in Android 3.0, when tablets were a thing, they were hastily put together as a kind of reusable mini-activities so that larger tablet layouts could display several of them simultaneously (think master-detail flows and such). Unfortunately, they inherited many design flaws of activities and even added some very interesting new ones. To say they are controversial would be an understatement.</p><p>On top of that, we don’t really need them in the way that we need that one launcher activity. Bigger, reusable pieces of UI can be served using good old views and there are 3rd party frameworks that do just that (and even several others that achieve the same thing in other ways, like using RecyclerViews to compose the UI from different “cells” etc.); and don't even forget that Jetpack Compose is coming...<br></p><p>However! Fragments are still developed, supported, documented, advocated for, integrated with other highly useful libraries (like Jetpack’s Navigation component) and in some places, they’re quite irreplaceable (yes, this is more of a design flaw of such APIs, but we need to work with what we have). Love them or hate them, they are the standard, well-known official solution, so let’s just be pragmatic: We’ll give them a hand, but won’t let them take the whole arm.</p><p>And so, we’ve arrived at the second question: One activity and possibly many fragments—but what can we use them <em>for</em>? And if we can, <em>should</em> we?</p><h2>Less is more</h2><p>This is where the opinions begin, so brace yourself.</p><p>Architecture-wise, what is an activity (and to an extent, a fragment, since they share many similarities)? My answer is this: A textbook violation of the single responsibility principle. </p><p>The main problem with an activity/fragment is that it is:</p><ol><li>a giant callback for dozens of unrelated subsystems</li><li>which is created and destroyed outside of our control</li><li>and we cannot safely pass around or store references to it.</li></ol><p>Typical consequences of these issues (when not handled in a sensible way) include activity subclasses several thousand lines long, full of untestable spaghetti (1), UI glitches and crashes (2) and memory leaks (3).</p><p>Open any activity in your current project, type <span class="pre-inline">this.</span> and marvel at the endless list of methods. The humble activity handles UI lifecycle and components, view models, data and cache directories, action bars and menus, assets and resources, theming, permissions, windows and picture-in-picture, navigation and transitions, IPC, databases and much, much more.</p><p>How Android got to this point isn’t important right now, but your code doesn’t have to suffer the same bloated fate. We need to chip away at the responsibilities and one way to do that is this: Use fragments exclusively for UI duties and that single activity for system call(back)s (and absolutely no UI).</p><h2>Fragments of imagination</h2><p>Each fragment should represent one screen (or a significant part of one) of your application. A fragment should only be responsible for </p><ol><li>rendering the view model state to the UI and</li><li>listening for UI events and sending them to its view model.</li></ol><p>That’s all. Nothing more. Really.</p><p>View model states should be tailored to concrete UI, should be observable and idempotent. It’s alright for view models and fragments to be quite tightly coupled (but view models mustn’t know anything about the fragments).</p><p>Because fragments are much harder to test than view models, the view model should pre-format the displayed data as much as possible, so the fragment can be kept extremely simple and just directly assign state properties to its view properties. There shouldn’t be any traces of formatting or any other logic in the fragments.</p><p>The opposite way should be equally simple—the fragment just attaches listeners to its views (our current favorite is the <a href="https://github.com/LDRAlighieri/Corbind">Corbind</a> library which transforms Android callbacks to handy and most importantly unified <span class="pre-inline">Flow</span>s) and sends these events directly to the view model.</p><p>That is what fragments should do. But what they shouldn’t do is perhaps even more important:</p><ul><li>Fragments shouldn’t handle navigation between screens, permissions nor any other system stuff, even if the APIs are conveniently accessible from right inside the fragment. </li><li>Fragments shouldn’t know about each other and shouldn’t depend on or interact with their parent activity. </li><li>Fragments shouldn’t know about how they are instantiated and how they are injected (if your DI framework allows this); they also shouldn’t know about fragment transactions, if possible. </li><li>Data should be passed to fragments <em>exclusively</em> through their view models and that should be just the data to be displayed in the UI—forget about fragment arguments and rich domain models in them. </li><li>This almost goes without saying, but fragments shouldn’t do any file, database or network IO (I know, inside the fragment, the almighty Context is sooooo close… Just a small peak into SharedPrefs, please? No, never!). </li><li>Since Android view models got <span class="pre-inline">SavedStateHandle</span>, fragments even shouldn’t persist their state to handle process death. </li><li>And for heaven’s sake, never ever use abominations such as headless or retained fragments. </li></ul><p>Some other tips include:</p><ul><li>Fragments should handle only the very basic lifecycle callbacks like <span class="pre-inline">onCreate</span>/<span class="pre-inline">onDestroy</span>, <span class="pre-inline">onViewCreated</span>/<span class="pre-inline">onDestroyView</span>, <span class="pre-inline">onStart</span>/<span class="pre-inline">onStop</span> and <span class="pre-inline">onPause</span>/<span class="pre-inline">onResume</span>. If you need the more mysterious ones, you’re probably going to shoot yourself in the foot in the near future. </li><li>If possible, don’t use the original <span class="pre-inline">ViewPager</span> with fragments—that road leads to madness and memory leaks. There's a safer and more convenient <span class="pre-inline">ViewPager2</span> which works much like <span class="pre-inline">RecyclerView</span>. </li><li>Make dialogs with <span class="pre-inline">DialogFragments</span> <em>integrated with Jetpack Navigation component</em>. It’s much easier to handle their lifecycle (those dismissed dialogs popping on the screen again after device rotation, anyone?) and they can have their own view models. This way, there’s almost no difference between part of your UI being a dialog or a whole screen. </li><li>Sometimes it’s OK for fragments to include other fragments (e.g., a screen containing a <span class="pre-inline">MapFragment</span>), but keep them separate—no direct dependencies and communication between them, no shared view models etc. </li><li>To make your life easier, your project probably has some sort of <span class="pre-inline">BaseFragment</span> which simplifies plumbing, sets up scopes, and what have you. That’s fine, but resist the temptation to pollute it with some “handy” little methods for random things like toasts, snackbars, toolbar handling etc. YAGNI! Don’t misuse inheritance as a means to share implementation—that’s what composition is for. </li><li>Our favorite way to access views from fragments is the relatively new and lovely ViewBinding library. It’s simple to integrate, straightforward to use, convenient, type-safe, and greatly reduces boilerplate. No other solution (findViewById, Butter Knife, kotlin-android-extensions plugin or Data Binding library) possesses all these qualities. </li><li>Speaking of Data Binding, even when it isn’t throwing a wrench into your build, we don’t think that making our XMLs smarter than they need to be is a good idea to begin with. And don’t let me start about the testability of such implementations. </li><li>Use <a href="https://github.com/square/leakcanary">LeakCanary</a>! The recent versions require practically no setup and automatically watch Activity, Fragment, View and ViewModel instances out of the box. </li></ul><p>After following all this advice (and a little bit of coding), your <em>complete</em> fragment could look like this (the implementation details aren’t important, just look at the amount and <em>intention</em> of the code):</p><pre> <code class="kotlin hljs">internal class ItemDetailFragment : BaseFragment<ImteDetailViewModel, ItemDetailViewModel.State, ItemDetailFragmentBinding>() { // take advantage of reduced visibility if possible so you don’t pollute your project’s global scope // required by DI framework override val viewModelClass = ItemDetailViewModel::class // layout inflation with the lovely ViewBinding library override fun onCreateViewBinding(inflater: LayoutInflater) = ItemDetailFragmentBinding.inflate(inflater) // initialization of view properties that cannot be set in XML override fun ItemDetailFragmentBinding.onInitializeViews() { detailContainer.layoutTransition?.disableTransitionType(DISAPPEARING) } // render the view model state in the UI; kept as simple as possible // state properties should preferably be primitive or primitive-like types // no DataBinding :) // notice the receiver - we don’t have to reference the binding on every single line override fun ItemDetailFragmentBinding.onBindState(state: ItemDetailViewModel.State) { loading.isVisible = state.isLoadingVisible itemTitle.text = state.item.title itemCategory.textResId = state.item.categoryResId itemFavorite.isChecked = state.item.isFavorite itemPrice = state.item.price // price is a String and is already properly formatted /* ... */ } // the other way around: catch UI events and send them to the view model override fun ItemDetailFragmentBinding.onBindViews() { toolbar.navigationClicks().collect { viewModel.onBack() } toolbar.menu.findItem(R.id.checkout).clicks().collect { viewModel.onCheckout() } favorite.checkedChanges().collect { isChecked -> viewModel.setFavorite(isChecked) } addToWishList.clicks().collect { viewModel.onAddToWishList() } addToCart.clicks().collect { viewModel.onAddToCart() } /* ... */ } } </code></pre><p>That’s not that bad, is it?</p><h2>If you can’t beat them, join them</h2><p>Although hardly an elegant or easy-to-use API, fragments are here to stay. Let’s make the best of this situation: Pragmatically utilize them for their useful integrations and focus on the single real responsibility they have—handling the UI. Ignore the rest and KISS—this principle is extremely important when working with fragments. That way, you’re going to have small, simple, focused fragments—and more importantly, a lot less less headaches.</p>#architecture;#android;#jetpack
SwiftUI and Architecture: Statehttps://mobileit.cz/Blog/Pages/swift-ui-and-architecture-state.aspxSwiftUI and Architecture: State<p>A new era has come to the mobile world. At least in the context of UI frameworks. What does this mean for your project? Will your architecture stand strong and allow for incremental adoption? Or will you have to resort to complete rewrite?</p> <h2>​​Welcome to the new old world</h2><p>The cornerstone of user interface development for iOS since its inception has been UIKit (or AppKit for macOS). It provides a mechanism to define window and view hierarchy, navigate between scenes, render content on the screen, and has tools for handling user interaction. UIKit was designed over a decade ago in the Objective-C world and leverages principles that were common back then, like delegates and inheritance. Functional elements of modern languages like Swift naturally lead to a declarative programming paradigm which is where SwiftUI steps in.</p><p>The SwiftUI framework was announced at WWDC 2019 and introduces the aforementioned declarative approach to writing UI code, favors composition, uses value types extensively and provides under the hood optimization. It completely changes how we reason about the UI and how we write the UI code. Its principles and design are inspired by React from the Javascript world, which dates back to 2013, so our thanks and a round of applause are in order for the JS community for working out its kinks!</p><p>Code written in SwiftUI is multiplatform and can be deployed to tvOS, macOS, and watchOS platforms. Interoperability with UIKit and AppKit helps with the transition even further.</p><p>Interestingly, a very similar approach also came to the Android world, known as Jetpack Compose. If you are more interested in this, it is well described in posts "Jetpack Compose: What you need to know", <a href="/Blog/Pages/compose-1.aspx"> <ins>part 1</ins> </a> and <a href="/Blog/Pages/compose-2.aspx"> <ins>part 2</ins> </a>, although since then some things inevitably changed. </p><p>Both of these technologies are ready for production use. Let's focus on what the arrival of SwiftUI means from the architectural point of view, especially regarding state.</p><h2>SwiftUI and state</h2><p>In SwiftUI, a view is a function of a state, not of a sequence of events changing some internal, encapsulated state. What does that mean in practice?</p><p>SwiftUI observes UI state and triggers re-rendering of the view on every change (with some smart optimizations happening under the hood). This is done by property wrappers that annotate UI state objects and/or Combine publishers. It's only up to you to model, hold, transform, compose and distribute the state.</p><p>The state can be perceived from many different angles, for example:</p><ul><li>what is rendered on the screen,</li><li>data that are persisted in the storage,</li><li>state that's spread across multiple screens or features.</li></ul><p>It is crucial to properly separate these kinds of states from each other, as some have a limited role in a merely encapsulated context, while others may span across multiple contexts and may serve the purpose of the source of truth.</p><p>Such a source of truth must be unambiguously defined and shared. Let's take a look at how the currently most discussed architectures approach this.</p><p> <strong>MVx architectures</strong></p><p>MVC was most commonly used in iOS applications back in the day. It got sidetracked with the advent of functional reactive programming and was replaced with MVVM and its derivative MVVM-C which specifies an additional layer for navigation. The place for View and Presentation logic is clear in those cases, but the Model layer has too many responsibilities, which might lead to big components that have too many concerns. In another case, the Model layer may contain only domain objects whereas the Presentation layer deals with networking, storage, business logic, and transformation of domain objects into renderable objects for the view on one side together with handling actions from the view on the other side. Huge ViewControllers with many responsibilities led to the joke saying MVC is short for Massive-View-Controller.</p><p>MVx architectures are not sufficient by their definition to design the whole system. They are merely a pattern for the presentation part of the system. Clean and robust applications may be written with MVVM as the only architecture tool in your toolbox but it requires strict discipline and proper decomposition. The architecture used often branches out into something more sophisticated than 3-layer architecture but with no clear boundaries that are defined somewhere between the lines.</p><p> <strong>Redux</strong></p><p>As the SwiftUI is inspired by React from the Javascript world, where React and Redux often go hand in hand, it's natural to think about how this is mirrored in mobile application development.</p><p>Redux is a mechanism to update and distribute state around. It cannot be considered as "architecture" as it does not answer the important questions</p><ul><li>how to protect the app from the outside world?</li><li>how to enable easy change of technologies and third-party dependencies?</li><li>how to define strict boundaries to isolate features?</li><li>where are the business rules defined?</li><li>which parts deal with navigation, presentation, networking, storage, user input, and so on?</li></ul><p>These discussed architectures do not address modeling and state sharing in complete detail, they are often either used in a poor way that is not very readable and sustainable, or in a way that inherently leads to a more complex architecture with a looser definition of responsibilities.</p><p>Although SwiftUI is a new player on the mobile playground, managing state is definitely not. Let's see if the old and traditional principles still have anything to offer in this new world.</p><h2>Lessons learned from history</h2><p>There are many patterns and principles that are timeless, namely two that enable to store and distribute state in a transparent way.</p><p> <strong>Repository pattern</strong></p><p>A repository is a design pattern that abstracts data access logic from business access logic by exposing the collection-like interface to access business entities. The actual implementation of the interface might communicate with the database, REST API, and what your project requires as long as it doesn't leak its details to the business domain interface.</p><p>Such a repository could easily be the source of the truth for each feature. Repositories obviously hold the state, but let's see how to distribute the same state to multiple parts of the application.</p><p> <strong>Dependency Injection</strong></p><p>Sharing state across features is as simple as sharing the same repository instance. Dependency injection frameworks help organize the dependency graph. Some use Service Locator patterns that abstracts instance resolution behind an abstraction layer. It reduces boilerplate code you would otherwise need with manual dependency injection. The resolution might cause runtime crashes if some requested instances are not registered. That's a downside that can you can handle with unit tests though.</p><p>You can also implement a shared repository using pure language without any dependency frameworks. The principle remains the same in both cases.</p><h2>Architecture and state</h2><p>It's important to design your system</p><ul><li>to be soft and malleable, so you're able to swiftly deliver changes that are desired,</li><li>but robust enough, so that you can be sure those changes are not going to cause unexpected behavior, </li><li>and clear enough so it's easy to navigate and doesn't grow over your head.</li></ul><p>Decoupling is critical to achieving that. To decouple views, view models, scenes, and what have you, you need to figure out how to share the source of truth across the app. If you mishandle it or don't think much about the state, you're going to have a hard time even with UIKit applications as it leads to coupling that results in code that's hard to test, read and reason about, for instance:</p> <img alt="MVVM" src="/Blog/PublishingImages/Articles/swift-ui-and-state-02.png" data-themekey="#" /> <p>ViewModels in this case do way too much. They pass state among each other which makes them difficult to test and harder to read. The source of truth for these data is also lost, it's unclear where the state originated and who owns it.</p><p>State management is not something new that came with SwiftUI, it's been here for decades, and there are no specific new architecture requirements. If you make features independent, inject your dependencies, test, and define the source of truth with a repository pattern, you can design your system to be easier to maintain and grow, regardless of which UI framework you pick.</p> <img alt="Clean Architecture" src="/Blog/PublishingImages/Articles/swift-ui-and-state-01.png" data-themekey="#" /> <p>There's a strict boundary between features in this example. ViewModels only transform data and actions between Views and UseCases. There is one clear source of truth for each feature. Isolation leads to reusability and testability.</p><p>State management was always important in app development, unfortunately often it was neglected or done poorly. Declarative UI concepts do not bring anything new to the table as they just explicitly accent what's important to handle well because the view is a function of a state.</p><h2>Old and new, together</h2><p>Breakthrough technologies such as SwiftUI don't come out that often, but that doesn't mean that your app and architecture should succumb to being completely dependent on the technology and/or derive itself from it. On the contrary, if you adhere to the separation of concerns and decouple your code, you achieve strictly defined boundaries and can start adopting those technologies without having to rewrite most of your app and even do so incrementally.</p><p>SwiftUI is a brand new technology, but that doesn't mean we can't benefit from timeless and classic principles that helped deal with many challenges over many decades. Old and new can go hand in hand together and complement each other to enable us to create brilliant apps.</p>​<br>#swiftui;#architecture;#state