DEV Community

Cover image for DSL (Domain-Specific Languages) - O início
Rafael Rodrigues de Oliveira
Rafael Rodrigues de Oliveira

Posted on • Edited on

DSL (Domain-Specific Languages) - O início

Recentemente, tenho mergulhado no mundo das DSLs, um tema que sempre me instigou e motivou a buscar um conhecimento mais aprofundado. Quero compartilhar esse conhecimento com vocês, para ajudar a disseminar essa abordagem.

Este artigo é o primeiro de uma série, que irá fornecer uma introdução ao assunto e abordar diversos conceitos relacionados a ele. Os próximos artigos irão se aprofundar nos tópicos apresentados.

Para aqueles que desejam se aprofundar ainda mais, gostaria de indicar dois livros que servem como base para a série que irei escrever.

É importante ressaltar que todos os exemplos de código apresentados serão em Java, mas os conceitos podem ser aplicados em qualquer linguagem de programação.

Então, vamos juntos explorar o fascinante mundo das DSLs!


A Linguagem Específica de Domínio

 
O que seria uma Linguagem Específica de Domínio?

Basicamente, é uma linguagem de programação criada para um domínio específico, com expressividade limitada.

Mas o que isso significa na prática? Bem, uma DSL é projetada para facilitar a compreensão por humanos, diferentemente de linguagens de propósito geral que possuem diversos recursos, como controle de fluxo e estrutura de dados. Na DSL, a ideia é que ela possua apenas os recursos necessários para o domínio em questão.

É importante destacar que você não irá construir um software inteiro em uma DSL, mas sim utilizá-la em pontos específicos do sistema. O foco é sempre no domínio em questão.

O benefício de construir uma DSL é a criação de uma linguagem de fácil compreensão tanto para especialistas do domínio quanto para desenvolvedores. É uma forma de se ter um vocabulário compartilhado e padronizado entre todas as pessoas envolvidas no projeto.

Portanto, se você precisa trabalhar em um domínio específico e quer tornar a comunicação mais eficiente, a criação de uma DSL pode ser uma ótima solução.


O Modelo

 
Se você está se aprofundando no mundo de DSLs, certamente já deve ter ouvido falar do Modelo Semântico. Mas, afinal, o que é isso?

Em resumo, o Modelo Semântico é o motor que fornece o comportamento da DSL. Ele define a semântica, ou seja, o que será executado quando o código for rodado.

E qual a relação da DSL com o Modelo Semântico? A resposta é simples: a DSL fornece maneiras de preencher esse modelo.

No entanto, é importante não confundir o Modelo Semântico com o Modelo de Domínio, este último sendo geralmente mais complexo. O Modelo Semântico é uma parte fundamental da construção de uma DSL e pode ser tão simples quanto uma estrutura de dados.

Embora seja possível criar uma DSL sem um Modelo Semântico, é recomendável utilizá-lo em quase todos os casos. Ele é uma das peças-chave para garantir que a sua DSL seja compreendida tanto pelos especialistas do domínio quanto pelos desenvolvedores.

Por fim, é importante entender que, para construir uma DSL, você precisará tanto de uma gramática (que define a ordem das palavras) quanto de uma semântica (que define o que será executado com essa ordem). Juntos, esses elementos formam uma linguagem específica para o seu domínio.


Legal! Queremos código

 
Que tal explorarmos um exemplo prático de DSL Interna?

Modelo Semântico

public class Email {
  private Remetente remetente;
  private List<Destinatario> destinatarios;
  private Assunto assunto;

public Email(Remetente remetente, 
             List<Destinatario> destinatarios, Assunto assunto) 
{
  this.remetente = remetente;
  this.destinatarios = destinatarios;
  this.assunto = assunto;
}

public boolean enviar(Mensagem mensagem) {
   // Lógica de envido de email...
}

}
Enter fullscreen mode Exit fullscreen mode
public class Destinatario {

private String email;

public Destinatario(String email) {
  this.email = email;
}

public String getEmail() {
  return email;
}

}
Enter fullscreen mode Exit fullscreen mode
public class Remetente {

private String email;

public Remetente(String email) {
  this.email = email;
}

public String getEmail() {
  return email;
}   
}
Enter fullscreen mode Exit fullscreen mode
public class Mensagem {

private String corpo;

public Mensagem(String corpo){
  this.corpo = corpo;
}

public String getCorpo() {
  return corpo;
}
}
Enter fullscreen mode Exit fullscreen mode
public class Assunto {

private String titulo;

public Assunto(String titulo) {
  this.titulo = titulo;
}

public String getTitulo() {
  return titulo;
}
}
Enter fullscreen mode Exit fullscreen mode

DSL Interna

public class EmailDSL {

private EmailDSL() {}

public static void email(String de, String[] para, String assunto, String corpo) {
  List<Destinatario> destinatarios = Arrays.asList(para)
                                   .stream()
                                   .map(email-> new Destinatario(email))
  .collect(Collectors.toList());

  new Email(new Remetente(de), destinatarios, new Assunto(assunto)).enviar(new Mensagem(corpo));  
}

public static String de(String de) {
  return de;
}

public static String[] para(String... para) {
  return para;
}

public static String assunto(String assunto) {
  return assunto;
}

public static String mensagem(String corpo) {
  return corpo;
}
}
Enter fullscreen mode Exit fullscreen mode
@Test
public void dslFuncaoAninhada() {
 email(
       de("remetente@email.com.br"), 
       para("destinatario1@email.com.br", "destinatario2@email.com.br"),
       assunto("E-mail Teste"),
       mensagem("Esse E-mail serve para teste do Modelo Semântico")
      );
}
Enter fullscreen mode Exit fullscreen mode

Neste artigo, apresentamos um exemplo simples de como uma DSL Interna pode ser construída usando funções aninhadas. No entanto, é importante ressaltar que existem outras formas de criar uma DSL, cada uma com suas próprias vantagens e desvantagens.

Ao longo desta série de artigos, iremos explorar algumas dessas outras formas de construir DSLs, de modo a ajudar você a escolher a melhor abordagem para o seu projeto.


DSLs Internas e DSLs Externas

 
As DSLs podem ser divididas em duas categorias principais: internas e externas.

As DSLs internas são construídas dentro de uma linguagem hospedeira, como no exemplo que apresentamos anteriormente em que utilizamos o Java como linguagem hospedeira. Já as DSLs externas possuem uma linguagem própria e independente da linguagem hospedeira. Essas DSLs externas são geralmente escritas em arquivos separados dos arquivos de código da linguagem de programação principal.

Embora não entraremos em detalhes mais aprofundados sobre as diferenças entre esses dois tipos de DSLs, é importante que você saiba que essas duas categorias existem e que cada uma delas possui vantagens e desvantagens dependendo do seu caso de uso.

Só vou focar nas DSLs Internas.


Nem tudo são flores

 
A utilização de uma DSL pode trazer benefícios significativos para a expressão de intenções em um determinado domínio, mas também é importante avaliar os custos associados à manutenção e treinamento de novos membros da equipe. É preciso ponderar a complexidade do domínio e o quanto uma DSL pode ajudar a melhorar a expressividade da linguagem.

Ao avaliar a construção de uma DSL, é fundamental pensar nos ganhos que ela pode proporcionar e também nas camadas adicionais que serão incluídas no sistema. Em alguns casos, pode ser mais vantajoso utilizar uma linguagem de propósito geral em vez de criar uma DSL.

Ao finalizar, esperamos que as informações apresentadas possam ajudar na compreensão sobre DSLs e auxiliar no processo de decisão sobre sua utilização em projetos de desenvolvimento de software.

Referência

Top comments (2)

Collapse
 
artsmandev profile image
Bruno Andrade • Edited

Fantástico.
É um assunto novo para mim.
Quando eu vejo as classes Remetente e Destinatario quase idênticas, sinto um cheiro de duplicação de código.
Por que não optou em usar Usuario, ou, Cliente?

Collapse
 
oliveirarafael profile image
Rafael Rodrigues de Oliveira

Como o domínio do exemplo seria o envio de uma mensagem via e-mail, então usei termos baseados nesse contexto. Basicamente, toda mensagem tem remetente, destinatarios, assunto e o corpo da mensagem, usar Cliente ou Usuário não seria termos comuns nesse domínio. Lembrando que a intenção de utilizar uma DSL é ter uma linguagem de fácil compreensão tanto para os especialistas do domínio quanto para desenvolvedores.

No caso de ter classes Remetente e Destinatario idênticas é mais por uma questão de exemplo.