Composite vs. Multi-Project Build With Multiple Services (Docker Images)

Hi,

I am (also) trying to understand the difference between Composite Builds and Multi-Projects Build. I am particularly interested in maintaining a mono-repo, which produces multiple services, i.e., docker images. Moreover, I would like to understand, how do best versioning of the generated services, i.e., docker images, i.e., git tag of the repository vs. versions of images (probably not the context of gradle?).

Basically, my monorepo looks like

monorepo
|-- lib/
|-- proto/
|-- service/
    |-- foo/ // depends on proto/
    |-- bar/ // depends on proto/ and lib/

The lib/ contains the domain-logic and proto/ the gRPC API, i.e., auto-generated java classes from proto files, we already discussed this here and there. :heart_suit:

As can be seen in the layout, I have two services (might become more), which depend on lib/ (domain-logic) or both lib/ and proto/ (gRPC API).

Consequently, I need the files (output?) of those other directories for compilation and finally, want to build Docker images. We are using Bitbucket pipelines, i.e., I don’t know where to draw the line between gradle and Bitbucket. But I assume, each service-folder needs its own Dockerfile?

And finally, what is the best practice to do versioning for the produces artefacts (Docker images)? Shall each artefact simply has the git tag of the whole monorepo, even if a service might not be affected?

Yes, it is much. I just would like to know how to sort things out and where to look for my issues. :slight_smile:

Thanks in advance and best, patient-coder.

EDIT
Okay, seems to me I require autonomous builds for the services, i.e., composite build is my choice then?

I am (also) trying to understand the difference between Composite Builds and Multi-Projects Build.

One of the main differences is, that you can open a build that is included into another build in your IDE independently and also build it completely independently, while with a multi-project build you always have all projects in the build. But even there you can just build one project and with configuration cache also the configuration time is not that relevant anymore, …

Keep in mind the original use-case for composite builds.
You have your project A and use a library B by depending on it from some repository.
Now you want to play with the very latest code of B in A, or want to implement a feature in B that is used in A.
One way (the legacy Maven way) is to code in B then manually publish to a local Maven repository, then go to A and code there, then go back to B, do some changes publish again, work on A again.
The better way (if the Gradle versions are compatible, optimally identical) is, to include the build of B into the build of A, then you get both projects in your IDE automatically, and whenever you build A, B is built automatically if necessary and so on.

Nowadays you can also use composite builds in one repository as a measure of organizing the build, like having one build for lib, one build for foo and one build for bar where the latter two include the build for lib.

What you must not do, is to have one build for foo and one build for bar and just add the lib directory in both builds as project using include, because one project should always only be part of exactly one build or you get into serious trouble.

So the multi-project build variant would be that foo, bar, and lib are all in one build.

We are using Bitbucket pipelines, i.e., I don’t know where to draw the line between gradle and Bitbucket.

Pretty much up to you where you draw the line.
I usually try to be as independent from any CI system as possible by having all build logic in Gradle and the CI system merely is a scheduler for when to trigger which Gradle tasks.
So I would probably use some Gradle plugin like the bmuschko docker plugin to build the docker images by invoking Gradle tasks.

But I assume, each service-folder needs its own Dockerfile?

That has nothing to do with Gradle, but yes, probably.

And finally, what is the best practice to do versioning for the produces artefacts (Docker images)? Shall each artefact simply has the git tag of the whole monorepo, even if a service might not be affected?

Pretty much holy war here too.

My personal take is, if something is in the same VCS repo, it should have the same version, so that tags, branches, etc. are valid for all the things in the repo.
Also the other way around, if things are version / branched / released / … together, they should reside in the same VCS repo.

Okay, seems to me I require autonomous builds for the services, i.e., composite build is my choice then?

If you need really autonomous builds, probably.
But even with a bit multi-project build you could say build me just project X and its dependencies.
:man_shrugging: