Gabi und Sascha
Kategorien : Alle | Berlin | Bücher | Fotografie | Java | Linkhalde | Weichware | Verfassung

based architecture is a current hype in architecture. I am therefore not yet clear with it from the point of architecture.

I'm not clear with it

The good
It reduces the complexity in the code artifacts for a simpler maintenance.
The bad
It increases complexity of governance of the software.
The ugly
It moves the complexity of the software to the network.

The ugly

11 years ago died. Wheeler formulated one of the most important phrases for software architectures:

All problems in computer science can be solved by another level of indirection, except of course for the problem of too many indirections.

I must ask myself: what are the benefits to add the network as a layer of indirections? Handling software with networks is not trivial. The network might be not available or under heavy load. Timeouts are commonplace and many developers have never heard of the design pattern.

The bad

Governance of software is on of the most underestimated factor. In bigger systems you can't do what you want. Rules have introduced, established and must be respected. Without these rules a growing software system will collapse after a while - or maintenance costs will increase dramatically.

REST based microservice architectures are related to systems. And SOA without a strong governance will create an epic cluster fuck also.

Fine grained microservices will increase the complexity of the governance. The diagrams to visualize are increasingly confusing. And we need visualizations to get an overview and understanding of the system. And as we know, a process diagram with more than 7 boxes/tasks/whatever has the tendency to create chaos in our head.

The good

Software artifacts gets smaller. A microservice implementation should not have more than a few hundred lines of code - howsoever you measure it. This is good for refactoring or throwing the code away and write it new from scratch.

But I'm not quite sure that this is really a benefit. Do you really rewrite an implementation? In normal cases it is not a very complex tasks to refactor a code system with a few hundred lines of code. The benefit of rewriting comes in when you switch the technology stack. With microservices this is not a huge change-it-all tasks but you can change one service after the other.

More often than rewriting or refactoring a microservice (that works) is to refactor the entire system. Here REST based microservice based only on can become a pain in the ass. Refactoring a not typed software system is a mess. Also the communicating of code intention without a type system. Without a type system also design and governance (see: The bad) can become very problematic.

Although some advocates of Microservice architectures do not want to read: exports not only JSON, but also XML hedged with or any other technology that simple adds type safety.

[7 ENTEN HÄNGEN AN SPIESSEN IN EINEM FENSTER. IM HINTERGRUND SIND ASIATISCHE KÖCHE AM ARBEITEN. AUFGENOMMEN DURCH EINE SCHEIBE IN EINE RESTAURANTKÜCHE NAHE MUSEUMSINSEL (BERLIN).]
 

Find the whole code for the proof of concept on Github

A proof of concept to use the incredible JCommander API from Cédric Beust together with CDI in a Java SE environment.

The goal was to use the commands API with the @Parameters annotation together with CDIs natural plugin API Instance.

This means, a lot of sub commands with different parameters can be executed by a main command from command line. The parameters of the main command should be injected to the sub command implementation. The sub command can have own, different parameters.

An example for such command system is git.

Example


    git -c author=sascha.kohlmann@example.com commit -am "a commit"

Implementation

The main command configuration

The class with the main method must contain @Produces annotated method which returns a static variable with the only MainConfiguration instance.


    public class Application {

        private final static MainConfiguration MAIN_CONFIG = new MainConfiguration();

        @Produces @Config
        private MainConfiguration configuration() {
            return MAIN_CONFIG;
        }

        public static final void main(final String... args) {
            // do something
        }
    }    

The main method contains only the initialization of the Weld-SE container and runs the application.


    public static final void main(final String... args) {        
        final Weld weld = new Weld();
        try (final WeldContainer container = weld.initialize()) {
            result = container.select(Application.class).get().run(args);
        }
    }

    String run(final String... args) {
        // the application
    }

MainConfiguration

The MainConfiguration class is a straight forward data holder. The configuration field are annotated with @Parameter.


    public class MainConfiguration {

        @Parameter(names = "-m")
        private String main;

        public String getMain() { return main; }
    }

The sub commands

Sub commands implements a common command API like the following:


    public interface Command {
        String execute();
    }

Implementations of the command must have at least the @Parameters type annotation with the names attribute given. They may have sub command specific parameters and the parameter data of the main command.


    @Parameters(commandDescription = "Simple sub command", commandNames = "subcmd")
    public class SubCommand implements Command {

        @Inject @Config
        private MainConfiguration mainConfig;

        @Parameter(names = "-p")
        private String parameter;

        @Override
        public String execute() {
            return this.mainConfig.getMain() + this.parameter;
        }
    }

After the CDI initialization all sub command with the injection point for the MainConfiguration contains now the static instance from the main class. But the configuration is yet not initialized. So having a method with @PostConstruct annotated, using the main configuration will not work. Also using the sub command specific parameters in such a method will not work.

The JCommander initialization follows in the next step. But before, we must enhance the Application class with the CDI plugin API.

Instance<Command>

The Application class gets an injectable Ìnstance<Command> field. This field will be filled by CDI after the Weld initialization with all available Command implementations.


    public class Application {

        @Inject
        private Instance<Command> commands;     

        public static final void main(final String... args) {
            // do something
        }
    }

JCommander initialization

The run method initialize the JCommander with the following steps:

  1. Create a new JCommander instance with the static MAIN_CONFIG:
    JCommander jc = new JCommander(MAIN_CONFIG);
  2. Add all instances of private Instance commands to the JCommander instance:
    this.commands.forEach(cmd -> jc.addCommand(cmd));

Afterwards, parse the command line arguments with the JCommander instance.


    jc.parse(args);

That's it.

Execute the sub command

The last step is now to get the parsed sub command name an fetch the sub command implementation from JCommander. Then call the execute() method.


    final String parsedCommand = jc.getParsedCommand();

    for (final Map.Entry cmdEntry : jc.getCommands().entrySet()) {
        final String name = cmdEntry.getKey();
        if (name != null && name.equals(parsedCommand)) {
            ((Command) cmdEntry.getValue().getObjects().get(0)).execute();
        }
    }

Testing

Testing ist straight forward:


    @Test
    public void test_sub_command_execution() {
        Application.main("-m", "main", "subcmd", "-p", "sub");
        assertThat(Application.result, is(equalTo("mainsub")));
    }

Conclusion

Using CDI and JCommander with complex commands in a Java SE environment is quite simple. Using the CDI natural plugin API (Instance) is also very simple. Together this is a strong duo to simplify the development of Java command line tools.


Find the whole code of the proof of concept on Github.