DEV Community ๐Ÿ‘ฉโ€๐Ÿ’ป๐Ÿ‘จโ€๐Ÿ’ป

Fernando Pineda
Fernando Pineda

Posted on

The Spring illness to Java developers

Here's what an anonymous spring developer once said ๐Ÿ˜…:

Everything should be a component.

If you have worked in any Java codebase using the spring framework, maybe you have found classes with some dependencies that can be avoided with minor changes. Let us see in the following example:

In order to do the code less verbose I will use lombok annotations like @AllArgsConstructor

@AllArgsConstructor
@Component
public class AccountAdapter implements AccountPort {

    private final RestClient restClient;
    private final AccountAdapterTranslator translator;

    public Account createAccount(final CreateAccountEntity createAccount) {
       CreateAccountRequest request = translator.convertEntityToRequest(createAccount);
       Response<CreateAccountResponse> response = restClient.createAccount(request).execute();
       return translator.convertResponseToEntity(response);
    }
}

@Component
@NoArgsConstructor
public class AccountAdapterTranslator {

    public CreateAccountRequest convertEntityToRequest(final CreateAccountEntity createAccount){
        return CreateAccountRequest.builder()
            .field1(createAccount.getField1())
            .field2(createAccount.getField2())
            ...
            .build();
    }

    public Account convertResponseToEntity(final Response<CreateAccountResponse> response){
        return Account.builder()
        .fieldX(response.getFieldX())
        .fieldY(response.getFieldY())
        ...
        .build();
    }
}
Enter fullscreen mode Exit fullscreen mode

I don't know if you see my point here, but why would be the AccountAdapterTranslator class a component?

Let's see how would be a possible unit test of the class AccountAdapter by a Java developer with the spring illness:

@ExtendsWith(MockitoExtension.class)
public class AccountAdapterTest{

    @Mock
    private RestClient restClient;

    @Mock
    private AccountAdapterTranslator translator;

    @InjectMocks
    private AccountAdapter adapter;

    @Test
    public void testCreateAccountSuccesfully_ShouldReturnAccount(){
        //SetUp data
        CreateAccountRequest request = CreateAccountRequest.builder().build();
        CreateAccountResponse bodyResponse = CreateAccountResponse.builder().build();
        RestCall<CreateAccountResponse> restCall = mock(RestCall.class);
        Response<CreateAccountResponse> restResponse = Response.success(bodyResponse);
        Account account = Account.builder().build();

        when(translator.convertEntityToRequest(any())).thenReturn(request);
        when(restClient.createAccount(any())).thenReturn(restCall);
        when(restCall.execute()).thenReturn(restResponse );
        when(translator.convertResponseToEntity(any())).thenReturn(account);

        //Execution
        Account result = adapter.createAccount(request);

        //Assertions
        assertEquals(result, account);

    }
}
Enter fullscreen mode Exit fullscreen mode

Oh God! ๐Ÿคฆ ยกKill me now!

You don't need to do everything in a spring manner, creating every class as a @Bean or a @Component. You are a Java developer, and there are better ways to do your everyday job.

Use the language constructs, there are static methods, static classes that can do the job; for example, one possible refactor may be the following:

@AllArgsConstructor
public class AccountAdapter implements AccountPort {

    private final RestClient restClient;

    public Account createAccount(final CreateAccountEntity createAccount) {
       CreateAccountRequest request = AccountAdapterTranslator.convertEntityToRequest(createAccount);
       Response<CreateAccountResponse> response = restClient.createAccount(request).execute();
       return AccountAdapterTranslator.convertResponseToEntity(response);
    }
}


@NoArgsConstructor(access = AccessLevel.PRIVATE)
public class AccountAdapterTranslator {

    public static CreateAccountRequest convertEntityToRequest(final CreateAccountEntity createAccount){
        return CreateAccountRequest.builder()
            .field1(createAccount.getField1())
            .field2(createAccount.getField2())
            ...
            .build();
    }

    public static Account convertResponseToEntity(final Response<CreateAccountResponse> response){
        return Account.builder()
        .fieldX(response.getFieldX())
        .fieldY(response.getFieldY())
        ...
        .build();
    }
}
Enter fullscreen mode Exit fullscreen mode

AccountAdapterTranslator is not a @Bean or a @Component, and it doesn't need to be. Just a class with static methods is enough, and you don't need to create a mock for testing purposes; just let their methods complete the execution.

Some of those things happen because sometimes developers tend to do things automatically. Thinking twice is an excellent strategy to do the best that you can do when developing a feature.

There are always better ways to do the job. It would help if you didn't forget that before being a Spring developer, you are a Java developer, and before that, you are a Developer, and your job is to solve problems.

What do you think about the Spring illness? What other things do you think the Spring illness does to Java Developers?

Top comments (4)

Collapse
 
nfrankel profile image
Nicolas Frankel

Are you really advocating for static methods? If yes, as you wrote:

Oh God, kill me now

If no, then your writing is pretty confused (or confusing).

  1. You don't need annotations to make a class a component. That's actually a bad way to go.
  2. You can keep the same classes, but don't register them all in the beans factory

Here's an old post of mine: The case for Spring inner beans. Nowadays, you can replace the XML with a configuration class. This way, you can keep a a testable design at every step, and register only the beans you want in the factory.

Collapse
 
_fpineda profile image
Fernando Pineda

Hello @nfrankel , thanks for your comment. I have been following you long time ago.

Yes, I advocate for static methods, that's the point of my article.

The expression:
"Oh God! ๐Ÿคฆ ยกKill me now!" is because of the ugly test and design of using every class as a @Component

Collapse
 
nfrankel profile image
Nicolas Frankel

I have been following you long time ago.

Thanks! But then, you know I cannot agree with you on static methods.

The cost of creating an instance method instead of a static method is marginal, and the benefit is that you can test its callers in isolation. If you feel it doesn't deserve such test, fine. But you might change your mind later, the software will evolve so you'll need to test it later, etc.

The only thing I agree with is to stop making every class a Spring bean.

Finally, avoid annotations when you can and don't use autowiring a.k.a. magic.

Thread Thread
 
_fpineda profile image
Fernando Pineda

Well, Nicolas, that's ok. I think it depends. If the static method has a bit of complexity that's better to do an Instance method to test it isolated, but if it's just a bypass like a method in the example and that's my point (My bad ๐Ÿ‘Ž, I didn't make it clear) on that kind of methods that don't do a complex functionality it's better to do just a static method.

Thanks for your comments.

We want your help! Become a Tag Moderator.
Fill out this survey and help us moderate our community by becoming a tag moderator here at DEV.