Fork me on GitHub
#polylith
<
2022-08-15
>
emccue13:08:48

having a strangely fun time trying to do polylith but with java modules

@JollyModule(ModuleKind.COMPONENT)
module dev.mccue.realworld.user {
    requires dev.mccue.jollylith;
    requires java.sql;
}
I'm not convinced the interface.clj has as much of a place (everything in java is behind a logical interface + there is the service provider thing), but still its interesting. Also the code layout for a pure java module project is mostly polylith friendly, but its cumbersome to make the folders for projects/components/bases and stuff - hence me making an annotation

emccue13:08:31

So I have this as "UserService"

package dev.mccue.realworld.user;

import javax.sql.DataSource;

public interface UserService {
    static UserService create(DataSource datasource) {
        return new UserServiceImpl(datasource);
    }

    sealed abstract class LoginFailure extends Exception {
        public static final class BadUsername extends LoginFailure {}
        public static final class BadPassword extends LoginFailure {}
    }

    User login(String username, String password) throws LoginFailure;

    sealed abstract class RegistrationFailure extends Exception {
        public static final class EmailTaken extends RegistrationFailure {}
        public static final class UsernameTaken extends RegistrationFailure {}
    }

    void register(
            String username,
            String email,
            String password
    ) throws RegistrationFailure;
}

emccue13:08:37

and then a UserServiceImpl inside of this package - but if I were to want to do swapping of implementation I think I would instead do the service provider thing where there is code like

ServiceLoader.load(UserService.class).findFirst().orElseThrow()
inside of create() and code like
module project {
    requires com.github.sormuras.bach;
    provides java.util.spi.ToolProvider with
      project.Build2;
}
inside of the module declaration

tengstrand15:08:39

Hi @U3JH98J4R! Interesting. If I was going to do it in Java I would almost copy the way it’s implemented in Clojure: • use the same directory structure: ◦ workspace ▪︎ components • user ◦ src ▪︎ bases • mybase ◦ src ▪︎ projects • userservice ◦ pom.xml • put each component interface in e.g. com.topns.user.User and the functionality it exposes would be put in “functions” = public static methods, that often delegate to implementing “namespaces” = classes under com.topns.user (the last name in a Clojure namespace is compiled to a Java class, so we have to mimic that). • a base like mybase would live in the namespace com.topns.mybase where its API could live in e.g. com.topns.mybase.Api and supporting “namespaces” also as classes undere com.topns.mybase . • each project would be a collection of all source directories of the selected bricks (components and often one base). Because Java only supports one namespace, you need solve that in some way, where one way is to use https://mvnrepository.com/artifact/org.codehaus.mojo/build-helper-maven-plugin Maven plugin. If I remember right, the Java modules didn’t solve this problem, because you can not use the same component in more than one module (I may have wrong, because I haven’t programmed in Java for eight years!).

emccue15:08:03

Right now this is sans maven, just with "bach" and the "module structure"

emccue15:08:01

> it exposes would be put in “functions” = public static methods, I don't like this one because Java is obtuse to work with in that way, but also its restricting the kind of interface you can provide

emccue15:08:40

> Because Java only supports one namespace, you need solve that in some way So with this system you can use javac like

javac --module-source-path . --module dev.mccue.realworld.main
and it will compile all dependent modules as part of its thing

emccue15:08:47

and either put the modules into folders that are called components/bases/etc OR put compontent/base in the package name OR add that as metadata to the module-info and shrug your shoulders, which is where im at

tengstrand15:08:32

Okay, so the modules are equivalent to Polylith projects - the place you choose which bricks to include?

emccue15:08:01

yeah - at least I think so

emccue15:08:05

module dev.mccue.realworld.main {
    requires dev.mccue.realworld.database;
    requires dev.mccue.realworld.user;
}

emccue15:08:24

so this requires these two bricks

emccue15:08:36

@JollyModule(ModuleKind.COMPONENT)
module dev.mccue.realworld.database {
    requires dev.mccue.jollylith;
    requires java.sql;
}

emccue15:08:00

and this brick is marked as a "component"

tengstrand15:08:25

Can you have multiple implementations of the same component, e.g. dev.mccue.realworld.user ? (to support swappable components)

emccue15:08:48

In two possible ways

emccue15:08:57

1. Put the different dev.mccue.realworld.user implementations in different places in the filesystem and use --module-source-path to affect which one is found during compilation

emccue15:08:53

2. Make explicit implementation modules dev.mccue.realworld.user.impl1 , dev.mccue.realworld.user.impl2 and have the api provided by dev.mccue.realworld.user delegate to them

tengstrand16:08:23

Do you also get the single development experience where you can work with all your code/bricks from one place?

emccue16:08:32

The second one is what this syntax is for

provides java.util.spi.ToolProvider with
      project.Build2;

emccue16:08:41

yeah so far seems like it works

emccue16:08:14

add every module as its own "module" in intelliJ and it figures itself out

emccue16:08:23

making a module path that has external modules is unsolved-ish. I can make a module path with ant or whatever and use $(cat path) on everything

emccue16:08:05

but that approach assumes that the whole project wants the same resolved dependencies for all artifacts so its...interesting

tengstrand16:08:16

It would be cool if you could create a working https://codebase.show/projects/realworld?category=backend&amp;language=java for the RealWorld.

emccue16:08:24

that is what i was aiming for

tengstrand16:08:01

Yes, I saw realworld in the package name.

tengstrand16:08:48

Now you just have to implement tools.deps for Java! 😉

emccue16:08:46

unironically

emccue16:08:06

I have a plan for that, somewhat

emccue16:08:20

https://get-coursier.io/ This exists and is pretty good, just married to scala at runtime

emccue16:08:19

I contacted a university to see if I could wrestle up a group of students to translate that to Java as a senior design project

emccue16:08:38

im hopeful

tengstrand16:08:53

Okay, smart idea.

emccue16:08:46

yep got green light from profs, present to students in a week. Trying to ego detach for the sake of making things happen

emccue16:08:38

but yeah, with external dependency resolution this will work. Modules are still 😬 levels of support in the ecosystem, but...

emccue16:08:16

and like mechanically they aren't solving much here other than 1. Making it clean 2. Convenient javac invocation 3. Declarative brick requires

emccue16:08:08

and all the people who think modules really aren't useful still have the same points so I doubt this in particular will catch on, but i want to see the demo through

tengstrand16:08:56

One way of approaching it could be to consume the Clojure implementation as a library from Java, and wrap each of the bricks with an interface in Java as a Polylith workspace, and when it works, replace one at a time with Java code.

tengstrand16:08:56

This is how you could convert a codebase from e.g. Java to Clojure, but the other way around!

emccue16:08:59

what never stops feeling strange is why im doing this - i used to write clojure for fun but now that i write clojure for work I write Java for fun

tengstrand16:08:34

The benefits become more obvious when you have more than a handful bricks and a service, but maybe you will be able to sell the idea.

tengstrand16:08:59

Okay, so this is just because you are curious to see how it would work out in Java, interesting!

emccue16:08:45

> but maybe you will be able to sell the idea. With Java, even ideas i have really good pitches for I am pitching to an empty room

😂 1
tengstrand16:08:43

I tried to pitch Polylith to a really senior Java developer, but every time I said something, he replied that he could do the same using Maven!

emccue16:08:42

so many people seem incapable of imagining a better world its crazy

emccue16:08:45

I have a post in there i wonder if you'll pick out, but like the community that does exist is focused on very silly things

emccue16:08:37

or things that feel like bigger problems than they are because of people like that senior java dev to whom "everything is fine"

emccue16:08:46

screaming noises

tengstrand16:08:18

People don’t like change, in general. Most people like to feel safe and comfortable. If you have programmed in Java for 10+ or 20+ years, then generating toString, equals and hashCode methods from your IDE has become things you just do, without reflecting, it’s part of your workflow.

tengstrand16:08:11

I guess this must have been by you “The verbosity of Java is one of the main reasons I like it so much.“!

tengstrand16:08:52

Ha ha, bad joke.

emccue16:08:39

we're too many layers deep in irony for me to tell.

emccue16:08:18

https://www.reddit.com/r/java/comments/wodoy4/comment/ikc1d2v/?utm_source=share&amp;utm_medium=web2x&amp;context=3 This is a good overview of what i care about though. In truth the language is fine and getting better. Its everything around the language

tengstrand16:08:46

Yes, it looks like Oracle has made a good job when they took over after Sun.