Skip to content

Instantly share code, notes, and snippets.

@mbloms
Created May 7, 2026 16:46
Show Gist options
  • Select an option

  • Save mbloms/a246a5327e1561c851dc78520f122923 to your computer and use it in GitHub Desktop.

Select an option

Save mbloms/a246a5327e1561c851dc78520f122923 to your computer and use it in GitHub Desktop.

Global Agents Instructions

Terminal commands

Shell compatibility

Be aware that the shell could be fish, not bash or zsh. Use fish-compatible syntax when running terminal commands, or run them in bash explicitly by wrapping them in bash -c '...'.

Pager

When running git commands, add the --no-pager flag to make sure the entire output is printed.

Java code guidelines

Code style preferences

  • Use final when possible, both for fields, local variables, and method parameters.
  • Annotate parameters with lomboks @NonNull and jspecify's @Nullable
  • Prefer Java Streams over for loops
  • Always put final before annotations
  • Prefer to put parameters on separate lines in method and record definitions, and align them vertically with the first parameter.
  • Put arguments on separate lines in method calls if they are too long.
  • When separating arguments on multiple lines, never leave the closing parenthesis alone on a separate line. Put it directly after the last argument, LISP-style.
  • Closing curly braces should always be on a new line. If a closing parenthesis follows it, it should be on the same line.
  • Never add verbose frames around code comments. Remove them if you see them.

Use Domain Driven Design

In all production code, use domain driven design.

Domain driven design divides the code into three layers usually modeled as three packages, api, domain, and infrastructure.

The domain layer is the core of the application. Code in the API and infrastructure layers depend on the domain layer, but dependencies in the other direction is forbidden.

An app either contains one of each, for example:

  • com.swedbank.payments.core.agreements.api
  • com.swedbank.payments.core.agreements.domain
  • com.swedbank.payments.core.agreements.infrastructure

Or, if the app implements functionality/APIs that are independant in nature, multiple in different packages, for example:

src/main/java/com/swedbank/payments/exp/privat/
├── ExpPaymentsPrivateApplication.java
├── accounts
│   ├── api
│   ├── domain
│   └── infrastructure
├── autogiro
│   ├── api
│   ├── domain
│   └── infrastructure
├── config
│   └── HttpClientConfig.java
├── featureflags
│   ├── domain
│   └── infrastructure
├── quickbalance
│   ├── api
│   ├── config
│   ├── domain
│   └── infrastructure
└── security
    ├── api
    ├── config
    ├── domain
    └── infrastructure

In the latter case, code in the API and infrastructure packages must not depend on code in other API or infrastructure packages. However, in some cases it may be justified to depend on code in outside domain packages.

Spring configuration classes can be put in a configuration package on the same level as these packages, or a configuration package nested in one of these.

api – The API layer

Implements the Rest API using Spring Controllers and API models with proper Swagger annotations and jakarta validation.

src/main/java/com/swedbank/payments/core/agreements/api/
├── InternationalPaymentAgreementsController.java
├── InternationalPaymentAgreementsExceptionHandler.java
└── model
    ├── AccountApi.java
    ├── AddressInformationApi.java
    ├── LastModifiedApi.java
    ├── agreementsdetail
    │   ├── AgreementByParts.java
    │   ├── AgreementInformation.java
    │   ├── AgreementPartApi.java
    │   └── AgreementSummaryApi.java
    ├── commonproduct
    │   ├── ProductApi.java
    │   ├── ProductPartApi.java
    │   ├── StandardPeriodicPriceApi.java
    │   └── StandardTransactionPriceApi.java
    ├── customer
    │   ├── BillingSettingsGetResponse.java
    │   ├── BillingSettingsPatchRequest.java
    │   ├── ContactInformationGetResponse.java
    │   ├── ContactInformationPatchRequest.java
    │   └── CustomerMetadataApi.java
    ├── customprices
    │   ├── CurrentCustomPrices.java
    │   ├── CustomPeriodicPriceApi.java
    │   ├── CustomPriceApi.java
    │   ├── CustomTransactionPriceApi.java
    │   └── FutureCustomPrices.java
    └── primitive
        ├── AgreementTypeApi.java
        ├── BillingMethodApi.java
        └── ProductIdApi.java

The controller in the API layer implements the endpoints by calling services or ports defined in the domain layer. The API layer handles mapping the api request models into the domain models and types passed into the domain layer, and then papping the return types or errors into response objects.

Mapping between API models and their corresponding domain models are implemented in the API models.

  • Instance methods named toDomain map the API model to a domain model.
  • Static methods named fromDomain takes the domain model as an argument and returns the API model.

If the mapping is more complicated, it can be implemented in a dedicated mapper class.

The API layer is responsible for translating the pure domain layer into an API, which is usually a REST API. It should not depend on the infrastructure layer.

domain – The domain layer

The core of the application. Implements all pure domain logic.

src/main/java/com/swedbank/payments/core/agreements/domain/
├── AgreementManagementService.java
├── AgreementPartsService.java
├── ProductInformationService.java
├── amount
│   └── Amount.java
├── exception
│   ├── MainframeException.java
│   └── ReferencedException.java
├── model
│   ├── account
│   │   ├── Account.java
│   │   ├── AccountNumber.java
│   │   └── ClearingNumber.java
│   ├── customer
│   │   ├── BankId.java
│   │   ├── CustomerNumber.java
│   │   └── CustomerReference.java
│   └── product
│       ├── PaymentDirection.java
│       ├── PeriodicPricingInformation.java
│       ├── ProductId.java
│       ├── ProductPart.java
│       ├── ProductPartId.java
│       ├── ProductPartName.java
│       └── StandardTransactionPrice.java
└── port
    ├── AgreementPartsFetchRepository.java
    ├── AgreementPartsUpdateRepository.java
    ├── BasicInformationFetchRepository.java
    ├── BasicInformationPatchRepository.java
    └── CustomerDataDeleter.java

Arbitrary raw types like Strings are avoided in favor of "domain models" with clear validation rules. Also implements exceptions for all faults that the API layer should be able to handle. Defines interfaces in the port package that abstract away implementation specific things and communicating with external dependencies. These "port" interfaces are implemented in the infrastructure layer and dependency injected into the services that use them.

The domain package is strictly forbidden from having dependencies to the API and infrastructure layers. It can use types and code from the java standard library, or external libraries like vavr where it fits, but must be decoupled from implementation details in the API layer and infrastructure layer.

infrastructure – The infrastructure layer

The infrastructure layer provides integration towards infrastructure and/or external dependencies and adapts them to our domain. The infrastructure layer can also implement adapters for things in external libraries that we don't want to depend on directly in our domain.

src/main/java/com/swedbank/payments/core/defined/creditors/infrastructure/
├── adapter
│   └── InternationalRecipientsAdapter.java
├── config
│   └── ServiceFunctionClientConfig.java
└── sfapi
    ├── ServiceFunctionClient.java
    ├── mapper
    │   └── recipients
    │       ├── RecipientListDomainMapper.java
    │       └── UTBListaBetMott07IndataBuilder.java
    ├── model
    │   ├── bank
    │   │   └── generated
    │   │       ├── UTBFragaIBANkontoIndata.java
    │   │       ├── UTBFragaIBANkontoOutdata.java
    │   │       ├── UTMHemtaBankIndata.java
    │   │       └── UTMHemtaBankOutdata.java
    │   └── recipients
    │       └── generated
    │           ├── UTBListaBetMott07Indata.java
    │           ├── UTBListaBetMott07Outdata.java
    │           ├── UTBUppdatMottagare01Indata.java
    │           └── UTBUppdatMottagare01Outdata.java
    └── responsehandler
        ├── FetchRecipientListException.java
        └── FetchRecipientListResponseHandler.java

The infrastructure layer "protects" the domain layer from ugliness in external dependencies so that the domain logic in services can stay pure. Code in the infrastructure layer can only be called through dependency injection via interfaces defined in the domain layer. The methods exposed to the domain layer through interfaces take domain types as arguments and return domain types.

The infrastructure layer also implements mapping between domain models and infrastructure models. This could be done in fromDomain/toDomain methods like in the API layer, but is usually better handled in dedicated Builder classes that take domain types and build the objects sent to libraries or external dependencies, and domain mapper classes that convert the infrastructure models/objects into domain types.

The infrastructure layer should not depend on the API layer.

Test code

  • Use JUnit 5
  • Use AssertJ for assertions
  • Use Mockito for mocking
  • When naming test methods use this convention:
    • givenSomePremise_when_methodToBeCalled_thenExpectedResult
    • Never change the case when quoting a method, class, or variable name. Instead, surround it with underscores.

Avoid adding unneccecary verbose comments.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment