Micro Profile JavaEE com Wildfly Swarm

Foi-se o tempo em que desenvolver uma aplicação JavaEE era algo extremamente burocrático e que tínhamos um servidor de aplicação inchado e com um consumo excessivo de recursos.

Hoje temos servidores que foram pensado na reutilização dos recursos e ainda temos a possibilidade de escolher quais módulos vamos inicializar junto com o servidor de aplicação, os chamados profiles. Mas e quando pensamos em micro-serviço? Como podemos trabalhar com micro-serviços no JavaEE?

A ideia do micro-serviço é de ter serviços isolados e independentes. Até aí tudo bem, podemos criar aplicações JavaEE separadas(isto é cada uma com seu .war). Mas como podemos fazer o deploy dessas aplicações?

Podemos ter um único contêiner (servidor de aplicação) para onde efetuamos o deploy de todas as aplicações. Mas com isso estamos indo de frente com a ideia de micro-serviço. Pois estamos gerando um ponto único de falha (SPOF).

Em outras palavras se precisarmos por alguma razão interromper o contêiner, todas as aplicações penduradas nele cairão também.

Outra alternativa seria cada serviço ter seu próprio contêiner, mas dessa forma estaríamos subutilizando o próprio contêiner. Pois nele temos diversos serviços rodando e que nossas aplicações não iriam precisar.

Fonte: http://wildfly-swarm.io

Fonte: http://wildfly-swarm.io

Se não tivessemos usando servidor de aplicação poderiamos usar um contêiner embutido na aplicação (Tomcat, Jetty, Undertown e etc..). Porém dessa forma perderiamos as facilidades que o servidor de aplicação nos proporcionam.

E aí que entra uma ferramenta nova para nos ajudar, Wildfly-Swarm.

Com ele podemos declarar quais “módulos” da plataforma JavaEE queremos utilizar.

A partir daí é desenvolver sua aplicação JavaEE normalmente e ao empacotar nossa aplicação, o wildfly-swarm pegará nosso pacote .war e irá embrulhar em um pacote dele com um contêiner micro para rodar nossa aplicação. Daí o nome de Micro-Profile.

Esse artefato final (o pacote que foi gerado a partir do nosso .war) é um arquivo .jar e podemos executar com um simples comando java -jar no nosso terminal.

Wildfly-Swarm usa o conceito de UberJar para gerar o artefato final. Que nada mais é do que um arquivo .jar que contém além do seu .war todas as dependências necessárias para que o wildfly-swarm consiga rodar.

Logo um UberJar é um arquivo .jar um pouco mais inchado mas somente com o necessário para rodar a aplicação.

Fonte: http://wildfly-swarm.io

Fonte: http://wildfly-swarm.io

O mínimo necessário para rodar o wildfly-swarm é JDK8 e Maven ou Gradle.

No wildfly-swarm temos o conceito de fraction, que nada mais é do que uma funcionalidade/configuração do servidor de aplicação. Na maioria das vezes um fraction pode ser comparado (mapeado) com um subsystem do servidor de aplicação (ex.: Datasource, Driver, Pool, socket-binding e etc…), temos outros casos em que um fraction é uma funcionalidade que antes não tínhamos (nativamente) no servidor de aplicação (Ex.: Jolokia, Spring, NetflixOSS ).

Para usar o wildfly precisamos importar um pom.xml do wildfly-swarm no nosso projeto, e adicionar um plugin que fará a geração do UberJar.

Nesse post vamos construir uma simples aplicação rest usando o wildfly-swarm. Vamos lá!

Com o projeto (web) maven criado precisamos importar o pom.xml do wildfly-swarm ao nosso projeto. Esse pom.xml é importado declarando uma denpendencia gerenciada e declarando que o escopo da mesma será import.

Para não ficar espalhando a versão do wildfly-swarm estamos utilizando por todo nosso pom.xml vamos declarar uma propriedade com ela. Além disso já vamos definir a versão do Java que iremos utilizar, e como se trata de um projeto web teoricamente precisariamos de um arquivo web.xml porém não é necessário para nosso caso. Então vamos definir que não deve ser gerado um erro caso não exista o arquivo web.xml.

<properties>
   <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
		<maven.compiler.source>1.8</maven.compiler.source>
		<maven.compiler.target>1.8</maven.compiler.target>
		<failOnMissingWebXml>false</failOnMissingWebXml>
		<version.wildfly.swarm>2016.9</version.wildfly.swarm>
</properties>

Agora vamos importar o arquivo pom.xml do wildfly-swarm que tem o nome de BOM (Bill of Materials).

<dependencyManagement>
  <dependencies>
    <dependency>
      <groupId>org.wildfly.swarm</groupId>
      <artifactId>bom</artifactId>
      <version>${version.wildfly.swarm}</version>
      <scope>import</scope>
      <type>pom</type>
    </dependency>
  </dependencies>
</dependencyManagement>

Além disso precisamos adicionar o plugin que irá gerar o UberJar baseado no nosso war.

<plugin>
  <groupId>org.wildfly.swarm</groupId>
  <artifactId>wildfly-swarm-plugin</artifactId>
  <version>${version.wildfly.swarm}</version>
  <executions>
    <execution>
      <goals>
        <goal>package</goal>
      </goals>
    </execution>
  </executions>
</plugin>

Precisamos também adicionar a dependência referente à api do JavaEE e esta será provida pelo wildfly-swarm.

<!--Java EE Api -->
		<dependency>
			<groupId>javax</groupId>
			<artifactId>javaee-api</artifactId>
			<version>7.0</version>
			<scope>provided</scope>
		</dependency>

E esse é o setup para utilizar o wildfly-swarm, ao final teriamos o nosso arquivo pom.xml mais ou menos da seguinte maneira:

<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
	<modelVersion>4.0.0</modelVersion>
	<groupId>br.com.caelum</groupId>
	<artifactId>livraria</artifactId>
	<version>1.0</version>
	<packaging>war</packaging>


	<properties>
		<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
		<maven.compiler.source>1.8</maven.compiler.source>
		<maven.compiler.target>1.8</maven.compiler.target>
		<failOnMissingWebXml>false</failOnMissingWebXml>
		<version.wildfly.swarm>2016.9</version.wildfly.swarm>
	</properties>

	<dependencyManagement>
		<dependencies>
			<dependency>
				<groupId>org.wildfly.swarm</groupId>
				<artifactId>bom</artifactId>
				<version>${version.wildfly.swarm}</version>
				<scope>import</scope>
				<type>pom</type>
			</dependency>
		</dependencies>
	</dependencyManagement>

	<dependencies>

		<!--Java EE Api -->
		<dependency>
			<groupId>javax</groupId>
			<artifactId>javaee-api</artifactId>
			<version>7.0</version>
			<scope>provided</scope>
		</dependency>

	</dependencies>

	<build>
		<finalName>livraria</finalName>
		<plugins>
			<plugin>
				<groupId>org.wildfly.swarm</groupId>
				<artifactId>wildfly-swarm-plugin</artifactId>
				<version>${version.wildfly.swarm}</version>
				<executions>
					<execution>
						<goals>
							<goal>package</goal>
						</goals>
					</execution>
				</executions>				
			</plugin>
		</plugins>
	</build>

</project>

Agora vamos adicionar as dependências que queremos do servidor de aplicação, para o nosso caso vamos adicionar, jax-rs, cdi, ejb, jpa, datasources.


		<!--Swarm Dependencies -->

		<dependency>
			<groupId>org.wildfly.swarm</groupId>
			<artifactId>jaxrs-cdi</artifactId>
		</dependency>

		<dependency>
			<groupId>org.wildfly.swarm</groupId>
			<artifactId>ejb</artifactId>
		</dependency>

		<dependency>
			<groupId>org.wildfly.swarm</groupId>
			<artifactId>jpa</artifactId>
		</dependency>

		<dependency>
			<groupId>org.wildfly.swarm</groupId>
			<artifactId>datasources</artifactId>			
		</dependency>		

Vamos começar a “codar” nosso projeto de livraria.

Vamos iniciar criando nossas classes de domínio Livro e Autor e seus respectivos DAOs

@Entity
public class Livro {
	
	@Id
	@GeneratedValue(strategy=GenerationType.IDENTITY)
	private Long id;
	private String titulo;
	private String descricao;
	
	@ManyToMany
	private List<Autor> autores;
	
	@Deprecated
	private Livro(){}
		
	public Livro(Long id, String titulo, String descricao, List<Autor> autores) {
		this.id = id;
		this.titulo = titulo;
		this.descricao = descricao;
		this.autores = autores;
	}
	
	
	public Long getId() {
		return id;
	}
	
	public String getTitulo() {
		return titulo;
	}
	
	public String getDescricao() {
		return descricao;
	}

	public List<Autor> getAutores() {
		return autores;
	}


	public void adicionaAutor(Autor autor) {
		this.autores.add(autor);
	}
	
}
@Entity
public class Autor {
	
	@Id
	@GeneratedValue(strategy=GenerationType.IDENTITY)
	private Long id;	
	private String nome;
	
	@Deprecated
	private Autor(){}
	
	public Autor(Long id, String nome) {
		this.id = id;
		this.nome = nome;
	}

	public Long getId() {
		return id;
	}

	public String getNome() {
		return nome;
	}
	
}
public class LivroDAO {

	@PersistenceContext
	private EntityManager manager;

	public Livro buscaLivroComId(Long id) {

		Livro livro = manager.createQuery("select l from Livro l left join fetch l.autores a where l.id = :id", Livro.class)
				.setParameter("id", id).getSingleResult();

		return livro;
	}

	public List<Livro> listaTodos() {
		return manager.createQuery("select l from Livro l left join fetch l.autores a", Livro.class).getResultList();
	}

	public void adicionar(Livro livro) {
		manager.persist(livro);
	}

	public void atualiza(Livro livro) {
		manager.merge(livro);
	}

}
public class AutorDAO {

	@PersistenceContext
	private EntityManager manager;

	public Autor buscaAutorComId(Long id) {
		return manager.find(Autor.class, id);
	}

	public void adiciona(Autor autor) {
		manager.persist(autor);
	}
	
}

Vamos criar uma classe para configurar o JAX-RS para receber requisições a partir da raiz:

import javax.ws.rs.ApplicationPath;
import javax.ws.rs.core.Application;

@ApplicationPath("/")
public class JaxRsConfiguration extends Application{
}

Agora que já temos como receber requisições vamos criar nossos recursos que serão expostos pela nossa api
LivroResource e AutorResource além disso já vamos marcar-los como EJB Stateless para que já tenhamos transação entre outras coisas disponíveis.

@Path("livros")
@Stateless
public class LivroResource {

	@Inject
	private LivroDAO livroDao;

	
	@Path("{id:\\d+}")
	@GET
	@Produces(MediaType.APPLICATION_JSON)
	public Livro buscaPorId(@PathParam("id") Long id){
		return livroDao.buscaLivroComId(id);
	}
	
	@GET
	@Produces(MediaType.APPLICATION_JSON)
	public List<Livro> listaDeLivros(){
		return livroDao.listaTodos();
	}
	
	
	@POST
	@Consumes(MediaType.APPLICATION_JSON)
	public Response adicionaLivro(Livro livro){
		
		livroDao.adicionar(livro);
		
		return Response
					.created(URI.create("/livros/" + livro.getId()))
					.entity(livro)
					.type(MediaType.APPLICATION_JSON)
				.build();
		
	}
	
	
}

Nesse recurso temos as seguintes URIs

  • /livrosGET (Retorna uma lista com todos os livros)
  • /livros/idGET (Retorna o livro com ID especificado)
  • /livrosPOST (Cria um novo livro)
@Path("livros/{livroId:\\d+}/autores")
@Stateless
public class AutorResource {
	
	@Inject
	private LivroDAO livroDao;
	
	@Inject
	private AutorDAO autorDao;
	
	@GET
	@Produces(MediaType.APPLICATION_JSON)
	public List<Autor> listaDeAutoresDoLivro(@PathParam("livroId") Long livroId){
		
		return livroDao.buscaLivroComId(livroId).getAutores();
		
	}
	
	@GET
	@Path("{autorId:\\d+}")
	@Produces(MediaType.APPLICATION_JSON)
	public Autor autorDoLivro(@PathParam("autorId") Long autorId){
		 
		return autorDao.buscaAutorComId(autorId);
	}
	
	@POST
	@Consumes(MediaType.APPLICATION_JSON)
	public Response adicionaAutor(@PathParam("livroId") Long livroId, Autor autor){
		
		Livro livro = livroDao.buscaLivroComId(livroId);
		
		autorDao.adiciona(autor);
		
		livro.adicionaAutor(autor);
		
		livroDao.atualiza(livro);
		
		return Response
				.created(URI.create("/livros/" + livroId + "/autores/" + autor.getId()) )
				.entity(autor)
				.type(MediaType.APPLICATION_JSON)
				.build();
	}

}

Nesse recurso temos as seguintes URIs

  • /livros/idDoLivro/autoresGET (retorna todos os autores de um livro especifico)
  • /livros/idDoLivro/autores/idDoAutorGET (retorna um autor especifico de um livro especifico)
  • /livros/idDoLivro/autoresPOST (cria um novo autor associado ao livro)

Agora que temos nosso projeto pronto temos que registrar um datasource e associa-lo no nosso arquivo persistence.xml . Para o nosso caso, esta configuração será feita em uma classe com um método main.

Porém para configurar o datasource precisamos dizer qual o driver, e no nosso caso será um driver para mysql.
para não precisarmos registrar um driver, podemos adicionar a dependência para esse driver diretamente no pom.xml

<dependency>
	<groupId>org.wildfly.swarm</groupId>
	<artifactId>mysql</artifactId>
	<version>${version.wildfly.swarm}</version>
</dependency>

Para que seja adicionado corretamente o driver para mysql precisamos excluir o driver default que vem quando adicionamos a dependência para JPA que é o H2. Para isso vamos alterar a dependência de JPA e remover o H2.

<dependency>
	<groupId>org.wildfly.swarm</groupId>
	<artifactId>jpa</artifactId>
	<exclusions>
		<exclusion>
			<groupId>org.wildfly.swarm</groupId>
			<artifactId>h2</artifactId>
		</exclusion>
	</exclusions>
</dependency>

(Se não fizermos essa configuração ao subirmos o wildfly-swarm iremos receber um erro, informando que esta rolando um conflito entre essas dependências H2, MYSQL).

Pronto agora vamos registrar nosso datasource dentro de um método main:

public class Boot {
	public static void main(String[] args) throws Exception {
		
		Swarm swarm = new Swarm(args);
		
		swarm.fraction(
				new DatasourcesFraction()
				.dataSource("swarmDs", (ds) -> {
					
					ds.driverName("mysql");
					ds.connectionUrl("jdbc:mysql://localhost/livraria_swarm?createDatabaseIfNotExist=true&&useSSL=false");
					ds.userName("root");
					
				}));
				
				swarm.start();
	}
}

Como estamos sobrescrevendo o comportamento default do wildfly-swarm precisamos ensinar ele como ele deve fazer o deploy da nossa aplicação (ou seja dentro do nosso .war quais classes devem estar disponíveis, quais arquivos e etc…).

Para fazer isso iremos usar uma biblioteca chamada ShrinkWrap da própria JBoss que serve para criar um pacote programaticamente, ela é muito utilizada quando estamos usando o Arquillian para testes de aceitação/integração.

Com ele podemos criar JARArchive (.jar) e WARArchive (.war). Além desses temos outros tipos mais especificos por exemplo RibbonArchive um archive especifico que pode ser registrar em aplicações baseadas em Ribbon, Secured que já injeta o arquivo keycloak.json e já configura a parte de seguraça.

Temos também um outro tipo especifico que é JAXRSArchive que é um archive que já configura o jax-rs e faz o binding para classe de configuração Application/@ApplicationPath. Vamos utilizar esse archive.

Além disso precisamos dizer que ao fazer o deploy da aplicação seja levado os arquivos persistence.xml e beans.xml necessários para o funcionamento da JPA e CDI.

O arquivo beans.xml deve estar dentro do diretório WEB-INF a partir do classpath e o arquivo persistence.xml, deve estar em META-INF a partir do classpath também. E precisaremos configurar isso também.

public class Boot {
	public static void main(String[] args) throws Exception {
		
		Swarm swarm = new Swarm(args);
		
		swarm.fraction(
				new DatasourcesFraction()
				.dataSource("swarmDs", (ds) -> {
					
					ds.driverName("mysql");
					ds.connectionUrl("jdbc:mysql://localhost/livraria_swarm?createDatabaseIfNotExist=true&&useSSL=false");
					ds.userName("root");
					
				}));
				
		swarm.start();
		
		JAXRSArchive deployment = ShrinkWrap.create(JAXRSArchive.class);
		
		ClassLoader classLoader = Boot.class.getClassLoader();
					
		deployment.addAsWebInfResource(classLoader.getResource("beans.xml"),"beans.xml");
		deployment.addAsWebInfResource(classLoader.getResource("persistence.xml"),"classes/META-INF/persistence.xml");
		
		deployment.addPackages(true, Package.getPackage("br.com.caelum.livraria"));
		deployment.addAllDependencies();
		
		swarm.deploy(deployment);
		
	}
}

Como ultima configuração precisamos indicar ao plugin do wildfly-swarm que ele não deve usar sua classe padrão para iniciar. Ele deve usar nossa classe Boot e com isso alterar o arquivo de manifesto para usar essa classe como ponto inical da nossa aplicação. Vamos alterar a declaração do plugin no arquivo pom.xml e adicionar a configuração “.

<plugin>
	<groupId>org.wildfly.swarm</groupId>
	<artifactId>wildfly-swarm-plugin</artifactId>
	<version>${version.wildfly.swarm}</version>
	<executions>
		<execution>
			<goals>
				<goal>package</goal>
			</goals>
		</execution>
	</executions>
	<configuration>
		<mainClass>br.com.caelum.livraria.Boot</mainClass>
	</configuration>
</plugin>

Para que seja gerado o UberJar devemos executar o goal package do maven (Ex.: mvn package). Ao termino dessa execução no diretório target teremos um arquivo livraria.war e um livraria-swarm.jar (entre outros arquivos).

O arquivo livraria-swarm.jar é o nosso UberJar.

Para executarmos podemos faze-lo pelo plugin do maven através de mvn wildfly-swarm:run ou executando o comando java -jar livraria-swarm.jar.

Dessa forma temos uma aplicação JavaEE somente com o mínimo necessário e um contêiner bem mais leve para rodar a mesma.

Aprendendo mais

Quer aprender mais sobre como utilizar a especificação JavaEE?

Não deixe de conferir nosso curso Plataforma JavaEE, nele abordamos jax-rs, jpa, cdi, ejb entre outros recursos da especificação JavaEE.

E aí o que você achou do wildfly-swarm?

Tags:

17 Comentários

  1. KIVER V T SILVA 19/10/2016 at 09:27 #

    Muito bom, não conhecia-o

  2. Altair Moura 19/10/2016 at 10:15 #

    Fernando, muito bom o post. Vou aproveitar para tirar uma dúvida.

    É possível realizar múltiplos deploy?

  3. Raphael Lacerda 19/10/2016 at 10:59 #

    Muito bom Fernando!!

    Fiz um post sobre o KumuluzEE, que é outra alternativa

    http://blog.caelum.com.br/conheca-o-kumuluzee-seu-novo-framework-para-microservices/

    Complementa bem este post!!

    Eu achei o Kumuluz mais fácil de configurar, aliás, não tem nem o que configurar!! hehehe

  4. Rafael Pestano 19/10/2016 at 11:24 #

    Legal o post e bem interessante o micro profile!

    Qual o problema de:

    “Outra alternativa seria cada serviço ter seu próprio contêiner, mas dessa forma estaríamos subutilizando o próprio contêiner. Pois nele temos diversos serviços rodando e que nossas aplicações não iriam precisar.”

    se os serviços que você não utiliza não são inicializados (ex: não usa ejb, o contêiner ejb não será estartado, o mesmo para CDI e aí por diante)

    No pior dos casos você terá subutilização de disco mas isso não é mais um problema, certo?

    Com relação a facilidade de execução via uber jar uma alternativa seria usar docker contêiners, dessa forma ao inicializar o contêiner docker a aplicação sobe.

    Como referencia segue um video do Adam Bien onde ele sobe application servers dentro de containers docker para fazer orchestração e comunicação de micro serviços: https://www.youtube.com/watch?v=QO2321eMNYE

    Apenas um contra ponto 😉

  5. Fernando Furtado 19/10/2016 at 12:22 #

    Muito obrigado Altair,

    E sobre ser possível múltiplos deploys, a ideia do wildfly-swarm é que você tenha somente um serviço para cada instancia, ou seja uma aplicação somente.

  6. Fernando Furtado 19/10/2016 at 12:22 #

    Obrigado Raphael, eu não conhecia o KumuluzEE vou dar uma olhada nele.

  7. cviniciusm 19/10/2016 at 16:58 #

    Olá,

    Muito bem.

    Tenho um projeto Java EE + JSF 2.2 + Primefaces 6.0 + CDI que está funcionando no Wildfly 10.1 .

    Criei o seguinte projeto baseado nesse para praticar o Wildfly Swarm: https://gitlab.com/cviniciusm/livraria-swarm

    Quando executo mvn clean wildfly-swarm:run aparece o erro abaixo:
    [INFO] Starting .jar
    Dependencies not bundled, will resolve from local M2REPO
    Exception in thread “main” java.lang.IllegalArgumentException: Resource should be specified
    at org.jboss.shrinkwrap.impl.base.Validate.notNull(Validate.java:43)
    at org.jboss.shrinkwrap.impl.base.container.WebContainerBase.addAsWebInfResource(WebContainerBase.java:403)
    at br.com.caelum.livraria.jsf.shrinkwrap.Main.main(Main.java:28)

    Quando executo java -jar target/livraria.jar aparece o erro abaixo:
    nenhum atributo de manifesto principal em target/livraria.jar

    Além disso o arquivo jar não tem os arquivos estáticos (*.xhtml)

    Sabe como resolver isso, por favor ?

  8. Fernando Furtado 20/10/2016 at 18:12 #

    Fala aí Rafael,

    Muito bem colocado seu ponto de vista, vou ver como consigo melhorar essa parte do post.

    Sobre rodar o uber jar via docker: Quando comecei a escrever o post minha ideia era no final subir um container rodando a aplicação, mas aí o post começou a ficar um tanto quanto grande, aí abortei a ideia (quem sabe num próximo). Muito bacana no video, e quem tem interesse recomendo.

    Muito obrigado pela sua colaboração.

  9. Fernando Furtado 20/10/2016 at 18:23 #

    Fala aí Cássio tudo bem?

    O que está acontecendo aí é que não está sendo encontrado algum arquivo no seu deploy.

    Olhando seu código, na linha 28 da sua classe main você tem a seguinte declaração deployment.addAsWebInfResource(classLoader.getResource("beans.xml"), "beans.xml");.

    Para que essa declaração funcione seu arquivo beans.xml deve estar na raiz do seu classpath (Ex.: src/main/resources).

    Ou você poderia alterar essa declaração para usar o arquivo no local atual dele: deployment.addAsWebInfResource(new File("src/main/webapp/WEB-INF/", "beans.xml"), "beans.xml").

    A mesma coisa para os demais arquivos que você está adicionando no seu deployment.

    Espero ter ajudado. =)

  10. Rodrigo Vieira 21/10/2016 at 08:31 #

    “Em outras palavras se precisarmos por alguma razão interromper o contêiner, todas as aplicações penduradas nele cairam também.”

    Não seria “cairão”? 😉

  11. Paulo Silveira 22/10/2016 at 17:24 #

    arrumado 🙂

  12. Caio 25/10/2016 at 22:11 #

    Muito bom o tutorial, recentemente fiz algumas pequenas aplicações usando wildfly-swarm e gostei bastante, uma outra alternativa, seria o SpringBoot, que também é baseado na mesma ideia só que voltado para a plataforma Spring tenho grandes sistemas atualmente desenvolvido com ele.

    Acredito que micro profile deixa o desenvolvimento JavaEE cada vez mais produtivo e menos burocrático na hora do deploy em produção.

    Parabéns pelo artigo.

  13. Fernando Furtado 26/10/2016 at 09:40 #

    Fala aí Caio, tudo bem?

    Tenho gostado bastante do wildfly-swarm justamente pelo fato que você citou, de ele agilizar o desenvolvimento e desburocratizar o deploy.

    O spring-boot é simplesmente animal, gosto bastante dele também e do fato de realmente não precisar me preocupar (muito) com configurações e etc.

    Obrigado!

  14. Natanael Correia 27/10/2016 at 09:26 #

    Achei interessante, mas acho que o springBoot é mais simples na parte de configuração.
    Quando você disse ter um container para cada serviço, em meu ponto de vista seria o ideal desse jeito eu iria tirar somente o serviço do ar para manutenção, etc…
    Mas nesse caso não seria mais custoso na parte de infraestrutura?

  15. André Justi 27/10/2016 at 17:21 #

    tem como disponibilizar esse código? github 😀

  16. DSCunha 05/05/2017 at 08:54 #

    Excelente !
    Parabéns pela iniciativa.

Deixe uma resposta