Layered Build and Packaging
Layers are a great way to manage builds, packages, versions, tests, deployment configurations, etc. Like Gradle, StrataCode lets you define build configuration using an object oriented, interpreted language. More concise, readable and flexible build configurations than XML. But StrataCode uses it's extended version of Java as the build configuration language so you do not have to learn Groovy and can leverage all of the built-in Java libraries you already know.
Just keep in mind, the code in the layer definition file runs at build/run time and so can't use the code in the layer itself. The layered, component framework you can use in build files reduces copying while improving customization potential for your builds. Layers make it easy to define simple modules - organized by file type or dependency rather than using a lot of project scaffolding. Modules are usually already organized by type so why not make them simpler? You'll end up with more modules that can be managed by more people.
Complete Control Over Your Code
StrataCode is more than just a build tool though. It's a complete code-processing environment with the ability to read and make incremental changes to your code. Unlike tools which manipulate your application's byte-code on-the-fly, you can debug the generated code and so can do more without creating crazy debugging scenarios. You have all of the runtime safety benefits of compiled Java code and avoid "untraceable code" side-effects created by some uses of aspect-oriented programming.
Full IDE For Editing Build Files
StrataCode build files are written in StrataCode and can be built and maintained using the same IDE.
Layer's can include maven libraries in one of two ways. Your projects can use maven itself for builds and/or installs of dependencies, then just configure the layer to use the resulting .jar in the classpath. You also can use StrataCode's built-in support for maven repositories and POM files, eliminating the need to install maven and make better use of layers for customization. The layered approach also makes it easy to swap back and forth between source and compiled formats on a component-by-component basis.
StrataCode Project File Organization
To start a new project with most systems, you use a "create project" wizard which generates the default project files from the configuration you specify. It's your job to figure out what files were generated, where the info you provided is now stored, and how to manage those configured values. Perhaps they are not the same from one developer to the other, or one deployment to the next. Maybe your framework stores these external properties in a separate file, or maybe not. If you're lucky there's some way to externalize them - via environment variables but it's always ad-hoc and varies from one framework to the next.
And the next time you upgrade your tool you may be required to manually update those files. Your intent for how you customized your project is buried. The way you want to organize your source files is defined by your framework, not by design of the developers for this particular type of project.
With StrataCode, when a layer which extends a base-layer, it picks up all of the previous layer's project files, which includes the definitions of file formats and a default directory organization for this layer. You only need to specify files or settings you need to change, and you can change pretty much anything in the downstream layer, although it's best to stick with 'additive changes', that do not take away from the published contract for upstream layers. With framework features, you can layer your configuration - so each layer contains only the properties you want to set in that project, inheriting the rest. StrataCode combines the layers and generates one or more standard project directories. So your deployment configuration and tooling from that point on can be the same. This pattern both supports today's project structures, and let's you incrementally adopt a better way. One that reduces the project configuration, and improves the relevance of any configuration that's actually there.
StrataCode has a simple command-line integration with git for managing layers. Using layer definition files, you can easily change how a layer of code is defined - source of binary. You can move layers from one git repository to another, only by updating layer definition files. Because these files are statically typed and support IDE based tooling, everything is easier to manage.
Some branches in git can be avoided by creating layers which patch or replace code, then merged when they no longer are needed. You might copy some files or just add fields and override methods, create new types, etc. It can be a nice way to build a new feature because your changes are kept separate and together for easier navigation. You reduce conflicts with others changing different aspects of the same types - which would mean the same files if you did not use layers.
Layers and Contracts
Each layer can be thought of as supplying a set of API contracts - classes, properties, and types. They can be delivered in source or binary form, as long as these types are available to upstream code. When you can isolate how dependencies affect developers or other members of the team, you can optimize workflows - minimizing merge conflicts and making it easier to maintain compatibility over the lifetime of the project. Layers let you separate contracts from classes, processes, and git-repositories. They let you manage contracts between dependencies better. Mix and match, move and reassemble, plug and play. Some simple use cases:
- have one version of a layer retrieve a package as source, the other via a maven artifact - switch back and forth based on the need to debug the artifact, versus need for faster compiles
- layers which do not modify contracts act just like filters - options, you can turn on/off
- use layers to build code quickly, then refactor into shared, client and server layers - where the overlap defines the synchronization layer
Options for Module Packaging
In most project and build configurations, each module or project contains a lot of scaffolding - directories (and features) you might not need for any particular type of module: tests, configuration, source, documents, documentation, etc. When a project has lots of files of different types, it makes sense to separate them by type first. It makes things easier to find and reduces the size of your directories.
Other modules may already be organized by their type - tests, documentation, configuration, may be separated from the code according to important "lifecycles". When you combine files with different lifecycles in the same module you run into problems. Perhaps they are managed by different people, released at different times, included in different packages, organized in different source control systems, etc.
For these simpler modules, layers eliminate the scaffolding. Simply extend a layer which defines the context for your source files. It will make sure the files are deployed correctly - i.e. only run in test mode, includes in the test.jar file etc.
Layers support more flexible ways to organize files by their lifecycle, and their dependencies. In this case, you invert the directory structure - so you have test, doc, configuration, etc. groups of layers, each of which contains a simple directory of files. Instead of one module which is tightly coupled, you have lots of modules which are loosely coupled.
When you have simpler and more flexible module structures, you can create projects that are easier to manage, not just for developers but throughout the organization. Everyone contributes their "slices" of the feature set. Framework code controls how those assets are deployed. Static typing and tools keep things structured in the same way a complex object-oriented application is structured. That helps detect conflicts and gives tools for fixing them. This lets developers more flexibly organize source code amongst different git repositories and move back and forth maintaining compatibility with upstream layers.
Anyone who has worked in a big Java application is aware of how difficult it can be to manage dependencies. You have to worry about not only your dependencies but the "transitive dependencies" - dependencies of your dependencies. How do you select compatible versions? How do you pick up the latest versions? How do you understand what's happening and resolve conflicts - to select the right version, even in the common case when you are inheriting conflicting rules for overlapping packages? When a critical security patch comes out, who do you trust to delegate your transitive dependencies so you pick up that patch as soon as necessary?
Maven provides a flexible but intricate system to specify, inherit, exclude and resolve conflicts between the versions you inherit from transitive dependencies. The order in which modules are sorted is determined by the order in which the dependency graph is traversed. That's based on the order of dependencies in the pom.xml file essentially.
Layers do essentially the same thing, but open up a couple of extra design strategies that are interesting. First the stacking order of your layers determines the precedence when there are conflicts. This order is basically what maven would do with the same modules, but there's more flexibility in controlling that order at the framework level as StrataCode offers options for sorting layers beyond dependencies. Layers are organized into framework, application, and configuration stacks which are independently sorted and can set priorities and exchange info over init, start, validate cycles to work out the best configuration. It's easy to trace that order in the IDE. If necessary, you just add a new dependencies to change the order of how you include transitive dependencies.
It's also easy to separate the transitive depenencies into a new layer - so you can include a package, but not include it's dependencies. Or switch transitive dependencies by switching a layer. That's a better alternative than excluding dependencies, since who knows when those will actually be required rules on your dependencies. This way, a required dependency is always included but these packages of dependencies can be included optionally. Dependencies are themselves just new, trivial modules without any code. With maven, you need always get the dependencyManagement whether you want it or not.
Read more and see examples in the documentation.