DEV Community

loading...
Cover image for DSL (Domain-Specific Languages) - O início

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

Rafael Rodrigues de Oliveira
Updated on ・4 min read

Nos últimos meses tenho me aprofundado no mundo de DSL, que sempre foi um tema que me instigou a querer conhecer mais. E agora eu quero compartilhar esse conhecimento com vocês para ajudar a disseminar esse assunto.

Nesse primeiro artigo vou fazer uma introdução, e ao longo de outros artigos vou me aprofundando nos conceitos que giram em torno desse tema.

No final vou deixar a referência de dois livros que é base para a série que irei escrever.

Os exemplos de código serão todos em Java, mas você pode usar na linguagem de programação que quiser.

Então vamos lá!!


A Linguagem Específica de Domínio

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

Basicamente é uma linguagem criada para um domínio específico com expressividade limitada.

Ok! Não disse muita coisa, vamos lá.

Uma DSL não deixa de ser uma linguagem de programação, já que será usada por humanos a fim de instruir um computador a executar algo. E como qualquer linguagem, a sua estrutura é projetada para facilitar o entendimento por humanos. Diferente de uma linguagem de propósito geral onde geralmente fornece muitos recursos, como: controle de fluxo, estrutura de dados e outras coisas, a DSL tem um mínimo de recursos necessários que são úteis em seu domínio, por isso a expressividade da linguagem precisa ser limitada.

Você não irá construir um Software inteiro em uma DSL, o que vai fazer é utilizar uma DSL para um ponto específico de um sistema.

Lembrando, o Foco é no Domínio.

O benefício de construir uma DSL é criar uma linguagem de fácil compreensão tanto para os especialistas do domínio quanto para desenvolvedores.


O Modelo

 
Um conceito importante que preciso destacar é o Modelo Semântico. Mas o que ele é?

O Modelo Semântico é o motor que fornece o comportamento. Ele define a semântica, ou seja, o que faz quando é executado.

Tá bom e a DSL?

A DSL fornece maneiras de preencher esse modelo.

Não se pode confundir o Modelo Semântico com o Modelo de Domínio, o Modelo Semântico é mais simples, podendo ser somente uma estrutura de dados, já o Modelo de Domínio tende a ser mais complexo. Eu sei, ficou bem básico essa explicação, mas não quero detalhar esse tópico agora, por enquanto vamos ficar só no básico.

Vale ressaltar que o Modelo Semântico é uma parte importante na construção de uma DSL. Você pode se deparar com DSLs que não usam um Modelo Semântico, porém é aconselhável quase sempre usá-lo.

Com isso, para construir uma DSL você tem gramática e semântica, a gramática é a ordem das palavras e a semântica é o que será executado com essa ordem.


Legal! Queremos código

 
Vou mostrar para vocês um exemplo de uma DSL Interna de envio de E-mail.

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

Esse exemplo é só uma introdução de uma DSL Interna usando funções aninhadas, existem outras formas de construir uma DSL. Ao longo da série de artigos vou abordando algumas delas.


DSLs Internas e DSLs Externas

 
Existem duas categorias de DSLs, as internas e as externas. As internas utilizam uma linguagem hospedeira, no caso do exemplo, foi utilizado o Java. As Externas utilizam uma linguagem própria não se limitando à linguagem hospedeira e ficam em arquivos separados dos arquivos de código de uma linguagem de programação. Não vamos entrar em muitos detalhes sobre isso, a ideia é mostrar que existem dos tipos de DSLs.

Só vou focar nas DSLs Internas.


Nem tudo são flores

 
Um dos problemas que eu vejo na utilização de uma DSL é a manutenção da linguagem, já que todos os desenvolvedores de uma equipe vão precisar ter o conhecimento necessário para projetar e manter uma DSL.

Outra questão é a entrada de um novo membro na equipe, além de ter o tempo de aprendizado no sistema como um todo, tem curva de aprendizado na construção de uma DSL que muitas vezes pode se tornar longa dependendo da experiência do desenvolvedor.

O que eu posso deixar como conselho é, avaliar a complexidade do domínio, caso algum pedaço do seu domínio seja difícil de expressar usando uma linguagem de propósito geral, talvez seja o momento de criar uma DSL para expressar melhor a intenção do que será executado.

Lembre-se, uma DSL adiciona uma camada a mais no seu sistema, sempre é válido avaliar o quanto vai ganhar ao adicionar essa camada.

Chegamos no final! Agradeço pela leitura e espero que tenham gostado.

Referência

Discussion (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 Author

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.