Modularizando tu proyecto Scala con sbt
⏱ Dedicación recomendada: 0 minutos
Esto considera el contenido visible y relevante, e ignora texto colapsado o marcado como opcional.
r8vnhill/echo-app-sbt
Antes de que una biblioteca crezca —antes de agregar pruebas o automatizar tareas—, es fundamental estructurar bien el proyecto. Si en la lección anterior aprendiste a crear un proyecto básico con sbt y a ejecutar tu primer programa en Scala 3, esta vez daremos un paso más allá: modularizar tu aplicación.
En esta lección aprenderás a organizar un proyecto multi-módulo con sbt, una práctica esencial para desarrollar bibliotecas reutilizables y aplicaciones escalables. A través de un ejemplo simple pero completo, veremos cómo:
- Definir múltiples subproyectos (
lib
yapp
) dentro de un mismobuild.sbt
. - Compartir configuraciones comunes entre módulos.
- Reutilizar lógica definida en un módulo desde otro.
- Ejecutar un subproyecto específico desde la línea de comandos.
Este enfoque modular te permitirá escribir código más limpio, reutilizable y fácil de mantener. Lo que comienza como un proyecto simple puede convertirse en una base sólida para bibliotecas profesionales. Paso a paso, construimos una arquitectura que Scala (hehe).
- Multi-módulo: Estructura de proyecto donde el código se divide en varios subproyectos (
lib
,app
, etc.), cada uno con su propia funcionalidad. Permite separar responsabilidades y facilita el mantenimiento y la escalabilidad del código. build.sbt
: Archivo de configuración principal de sbt. Define los módulos del proyecto, sus dependencias, configuraciones compartidas y más.- Paquete: Estructura jerárquica utilizada para organizar el código en namespaces. En Scala se suelen usar dominios invertidos, como
com.github.username.echo
.
🏗️ Estructura esperada del proyecto
Antes de configurar el build.sbt
, es importante visualizar cómo estará organizado el proyecto. Nuestro objetivo es dividirlo en dos subproyectos: una biblioteca (lib
) que contendrá la lógica de negocio reutilizable, y una aplicación (app
) que funcionará como punto de entrada y consumirá esa biblioteca.
La siguiente estructura refleja esta separación, mostrando cómo se distribuyen los archivos fuente dentro de cada módulo y cómo se conectan entre sí:
Este proyecto está dividido en dos módulos: una biblioteca (lib
) y una aplicación (app
). Ambos son definidos en el archivo raíz build.sbt
, lo que permite que compartan configuraciones y se compilen como parte del mismo proyecto.
lib/
contiene la lógica de negocio reutilizable, organizada en el paquetecom.github.username.echo
.app/
define la aplicación que importa y utiliza la funcionalidad delib
, dentro del mismo paquete para mantener consistencia.EchoMessage.scala
representa una función o clase reutilizable en la biblioteca.App.scala
actúa como punto de entrada de la aplicación.- El subproyecto
app
declara una dependencia directa sobrelib
, lo que permite acceder a su código sin necesidad de duplicación.
Esta estructura modular refleja buenas prácticas de diseño en proyectos reales, donde la separación entre lógica de negocio y lógica de ejecución permite mayor claridad, mantenibilidad y escalabilidad.
src/
que habíamos creado antes?Puedes eliminar el directorio src/
que creaste en la lección anterior, o guardarlo como recuerdo.
📦 Paso 1: Crear la estructura de carpetas
- Windows
- macOS
- Ubuntu/Debian
Te recomendamos descargar el script directamente desde tu terminal:
$path = 'scripts/windows/Initialize-ScalaModules.psm1'; `
md (Split-Path $path) -f > $null; `
iwr "https://raw.githubusercontent.com/r8vnhill/echo-app-sbt/refs/heads/main/$path" `
-OutFile $path
md
(New-Item -ItemType Directory
): Crea el directorioscripts/windows
si aún no existe.-f
(-Force
): Ignora errores si el directorio ya existe.> $null
: Redirige la salida a$null
para evitar mostrar mensajes innecesarios.
iwr
(Invoke-WebRequest
): Descarga el script desde GitHub.-OutFile
: Especifica la ruta donde se guardará el archivo descargado.
Ahora puedes hacer las funciones del script disponibles en tu terminal de PowerShell. Para ello, ejecuta el siguiente comando:
Import-Module .\scripts\windows\Initialize-ScalaModules.psm1
# Y ver la documentación del comando que importamos:
Get-Help Initialize-ScalaModules -Examples
Y si quieres eliminar el comando del scope de tu terminal, puedes usar el siguiente comando:
Remove-Module Initialize-ScalaModules
Al desasociar el comando, evitas que permanezca en el scope de tu terminal, lo que puede ayudar a prevenir conflictos con otros comandos o scripts que puedan tener el mismo nombre.
Script de inicialización
function Script:Get-PackagePath([string[]]$PackageParts) {
$path = $PackageParts[0]
for ($i = 1; $i -lt $PackageParts.Count; $i++) {
$path = Join-Path $path $PackageParts[$i]
}
return $path
}
Esta función auxiliar toma un arreglo de segmentos de paquete (como @('com', 'example', 'app')
)
y los concatena en una ruta de sistema de archivos usando Join-Path
.
Se utiliza para construir rutas como:
"src/main/scala/com/example/app"
de forma segura y multiplataforma.
function Script:New-ScalaDirectory {
[CmdletBinding(SupportsShouldProcess)]
param (
[string]$Path,
[string]$Description
)
if ($PSCmdlet.ShouldProcess($Path, "Create $Description directory")) {
New-Item -Path $Path -ItemType Directory -Force -Confirm:$false | Out-Null
}
}
Esta función crea una carpeta con una descripción personalizada si el usuario acepta la operación. Usa ShouldProcess
para soportar los parámetros comunes -WhatIf
y -Confirm
, lo que permite ejecutar el script de forma más segura.
Se emplea dentro del proceso de inicialización para crear las carpetas correspondientes a los módulos app
y lib
.
function Script:New-ScalaFile {
[CmdletBinding(SupportsShouldProcess = $true)]
param (
[string]$Path,
[string]$FileName
)
if ($PSCmdlet.ShouldProcess($Path, "Create $FileName file")) {
New-Item -Path $Path -ItemType File -Force -Confirm:$false | Out-Null
Write-Verbose "Created $FileName at $Path"
}
}
Esta función crea un archivo vacío con el nombre y ruta especificados, ideal para scaffolding de proyectos Scala. Usa ShouldProcess
para que puedas simular la operación con -WhatIf
o confirmarla con -Confirm
. El mensaje Write-Verbose
entrega feedback si ejecutas el comando con -Verbose
.
function Initialize-ScalaModules {
[CmdletBinding(SupportsShouldProcess)]
param (
[string[]]$BasePackage = @('com', 'github', 'username'),
[string[]]$AppPackage = @('app'),
[string[]]$LibPackage = @('lib'),
[string] $AppFileName = 'App.scala',
[string] $LibFileName = 'Lib.scala'
)
$basePath = Get-PackagePath -PackageParts @('src', 'main', 'scala') + $BasePackage
$appPath = Join-Path 'app' (Join-Path $basePath (Get-PackagePath $AppPackage))
$libPath = Join-Path 'lib' (Join-Path $basePath (Get-PackagePath $LibPackage))
$appFile = Join-Path $appPath $AppFileName
$libFile = Join-Path $libPath $LibFileName
Write-Verbose "App directory: $appPath"
Write-Verbose "Lib directory: $libPath"
Write-Verbose "App file: $appFile"
Write-Verbose "Lib file: $libFile"
New-ScalaDirectory -Path $appPath -Description 'app'
New-ScalaDirectory -Path $libPath -Description 'lib'
New-ScalaFile -Path $appFile -FileName $AppFileName
New-ScalaFile -Path $libFile -FileName $LibFileName
}
Esta función crea la estructura básica para un proyecto Scala multi-módulo.
- Permite configurar paquetes base personalizados (
BasePackage
) y submódulos (AppPackage
,LibPackage
). - Usa funciones auxiliares reutilizables (
New-ScalaDirectory
,New-ScalaFile
) que soportan los parámetros-WhatIf
y-Confirm
. - Muestra información detallada si se invoca con
-Verbose
.
Ideal para inicializar proyectos de forma rápida y controlada.
$basePackage = @('com','github','username')
Initialize-ScalaModules -BasePackage $basePackage `
-AppPackage @('app') `
-LibPackage @('lib') `
-AppFileName 'App.scala' `
-LibFileName 'Lib.scala' `
-Verbose
Te recomendamos descargar el script directamente desde tu terminal:
file_path="scripts/unix/initialize_scala_modules.sh"
mkdir -p "$(dirname "$file_path")"
curl -fsSL "https://raw.githubusercontent.com/r8vnhill/echo-app-sbt/refs/heads/main/$file_path" \
-o "$file_path"
chmod +x "$file_path"
mkdir
: Crea el directorioscripts/macos
si aún no existe.-p
: Ignora errores si el directorio ya existe.$(dirname "$path")
: Extrae el directorio del path especificado.
curl
: Descarga el script desde GitHub.-o
: Especifica la ruta donde se guardará el archivo descargado.-fsSL
: Asegura que la descarga sea silenciosa y siga redirecciones.
chmod +x
: Cambia los permisos del archivo para hacerlo ejecutable.
Ahora puedes hacer las funciones del script disponibles en tu terminal de bash. Para ello, ejecuta el siguiente comando:
source scripts/unix/initialize_scala_modules.sh
# Y ver la documentación del comando que importamos:
initialize_scala_modules --help
Y si quieres eliminar el comando del scope de tu terminal, puedes usar el siguiente comando:
unset -f initialize_scala_modules
Al desasociar el comando, evitas que permanezca en el scope de tu terminal, lo que puede ayudar a prevenir conflictos con otros comandos o scripts que puedan tener el mismo nombre.
Script de inicialización
get_package_path() {
local IFS="/"
echo "$*"
}
Esta función toma una lista de elementos (por ejemplo, partes de un paquete como com github username
) y los concatena usando /
como separador, construyendo así una ruta tipo com/github/username
.
El truco está en la línea local IFS="/"
, que cambia el separador interno de campos de Bash. Luego, al expandir "$*"
, Bash une todos los argumentos con ese separador en una sola cadena.
Es una forma idiomática y segura de construir rutas sin usar bucles ni printf
.
create_scala_item() {
local action="$1" # Comando a ejecutar, como mkdir o touch
local action_arg="$2" # Argumento opcional, como -p para mkdir
local file_path="$3" # Ruta del archivo o directorio a crear
local label="$4" # Etiqueta descriptiva (para mensajes)
local emoji="$5" # Emoji representativo
local name="${6:-$file_path}" # Nombre a mostrar (opcional)
$verbose && echo -e "${BLUE}${emoji} Creating $label: $name${RESET}"
if $what_if; then
echo -e "${YELLOW}🔍 WhatIf: $action${action_arg:+ $action_arg} '$file_path'${RESET}"
return
fi
if $confirm; then
echo -ne "${CYAN}Create $label '$file_path'? [y/N] ${RESET}"
read -r reply
[[ "$reply" =~ ^[Yy]$ ]] || return
fi
if [[ -n "$action_arg" ]]; then
"$action" "$action_arg" "$file_path"
else
"$action" "$file_path"
fi
}
new_scala_directory() {
create_scala_item "mkdir" "-p" "$1" "directory" "📁"
}
new_scala_file() {
create_scala_item "touch" "" "$1" "file" "📄" "$2"
}
En este fragmento definimos una función genérica create_scala_item
para reducir duplicación al crear archivos y directorios. Recibe el comando a ejecutar (mkdir
, touch
), su argumento opcional (como -p
), y etiquetas para personalizar los mensajes de confirmación y verbose.
Luego, las funciones new_scala_directory
y new_scala_file
usan esta función para crear directorios y archivos respectivamente, manteniendo el mismo comportamiento configurable mediante --confirm
, --what-if
y --verbose
.
Además, usamos ${var:+ value}
para mostrar opcionalmente el argumento solo si está presente, y ${var:-fallback}
para mostrar un nombre alternativo si no se proporciona explícitamente.
initialize_scala_modules() {
# ... Configuration variables and argument parsing ...
# 📦 Construct the project source paths
local scala_path
scala_path=$(get_package_path "src" "main" "scala" "${base_package[@]}")
local app_path="app/$scala_path/$(get_package_path "${app_package[@]}")"
local lib_path="lib/$scala_path/$(get_package_path "${lib_package[@]}")"
local app_file="$app_path/$app_file_name"
local lib_file="$lib_path/$lib_file_name"
# 🐛 Verbose diagnostics
$verbose && {
echo -e "${GREEN}📦 App: $app_file${RESET}"
echo -e "${GREEN}📦 Lib: $lib_file${RESET}"
}
# 🛠️ Create directories and files
new_scala_directory "$app_path" "app"
new_scala_directory "$lib_path" "lib"
new_scala_file "$app_file" "$app_file_name"
new_scala_file "$lib_file" "$lib_file_name"
}
Esta función arma toda la estructura de carpetas y archivos para un proyecto Scala modular.
- Utiliza
get_package_path
para construir la ruta base del código fuente ensrc/main/scala
. - Luego concatena los subpaquetes específicos de
app
ylib
, así como los nombres de archivo indicados. - Si está activado el modo
--verbose
, imprime las rutas generadas para que puedas ver qué se va a crear. - Finalmente llama a funciones auxiliares que crean los directorios y archivos, respetando los flags
--confirm
y--what-if
.
Esta es la función principal que traduce las configuraciones del usuario en acciones concretas sobre el sistema de archivos.
base_package=('com' 'github' 'username')
initialize_scala_modules \
--base-package "${base_package[@]}" \
--app-file-name 'App.scala' \
--lib-file-name 'Lib.scala' \
--verbose
Te recomendamos descargar el script directamente desde tu terminal:
file_path="scripts/unix/initialize_scala_modules.sh"
mkdir -p "$(dirname "$file_path")"
curl -fsSL "https://raw.githubusercontent.com/r8vnhill/echo-app-sbt/refs/heads/main/$file_path" \
-o "$file_path"
chmod +x "$file_path"
mkdir
: Crea el directorioscripts/macos
si aún no existe.-p
: Ignora errores si el directorio ya existe.$(dirname "$path")
: Extrae el directorio del path especificado.
curl
: Descarga el script desde GitHub.-o
: Especifica la ruta donde se guardará el archivo descargado.-fsSL
: Asegura que la descarga sea silenciosa y siga redirecciones.
chmod +x
: Cambia los permisos del archivo para hacerlo ejecutable.
Ahora puedes hacer las funciones del script disponibles en tu terminal de bash. Para ello, ejecuta el siguiente comando:
source scripts/unix/initialize_scala_modules.sh
# Y ver la documentación del comando que importamos:
initialize_scala_modules --help
Y si quieres eliminar el comando del scope de tu terminal, puedes usar el siguiente comando:
unset -f initialize_scala_modules
Al desasociar el comando, evitas que permanezca en el scope de tu terminal, lo que puede ayudar a prevenir conflictos con otros comandos o scripts que puedan tener el mismo nombre.
Script de inicialización
get_package_path() {
local IFS="/"
echo "$*"
}
Esta función toma una lista de elementos (por ejemplo, partes de un paquete como com github username
) y los concatena usando /
como separador, construyendo así una ruta tipo com/github/username
.
El truco está en la línea local IFS="/"
, que cambia el separador interno de campos de Bash. Luego, al expandir "$*"
, Bash une todos los argumentos con ese separador en una sola cadena.
Es una forma idiomática y segura de construir rutas sin usar bucles ni printf
.
create_scala_item() {
local action="$1" # Comando a ejecutar, como mkdir o touch
local action_arg="$2" # Argumento opcional, como -p para mkdir
local file_path="$3" # Ruta del archivo o directorio a crear
local label="$4" # Etiqueta descriptiva (para mensajes)
local emoji="$5" # Emoji representativo
local name="${6:-$file_path}" # Nombre a mostrar (opcional)
$verbose && echo -e "${BLUE}${emoji} Creating $label: $name${RESET}"
if $what_if; then
echo -e "${YELLOW}🔍 WhatIf: $action${action_arg:+ $action_arg} '$file_path'${RESET}"
return
fi
if $confirm; then
echo -ne "${CYAN}Create $label '$file_path'? [y/N] ${RESET}"
read -r reply
[[ "$reply" =~ ^[Yy]$ ]] || return
fi
if [[ -n "$action_arg" ]]; then
"$action" "$action_arg" "$file_path"
else
"$action" "$file_path"
fi
}
new_scala_directory() {
create_scala_item "mkdir" "-p" "$1" "directory" "📁"
}
new_scala_file() {
create_scala_item "touch" "" "$1" "file" "📄" "$2"
}
En este fragmento definimos una función genérica create_scala_item
para reducir duplicación al crear archivos y directorios. Recibe el comando a ejecutar (mkdir
, touch
), su argumento opcional (como -p
), y etiquetas para personalizar los mensajes de confirmación y verbose.
Luego, las funciones new_scala_directory
y new_scala_file
usan esta función para crear directorios y archivos respectivamente, manteniendo el mismo comportamiento configurable mediante --confirm
, --what-if
y --verbose
.
Además, usamos ${var:+ value}
para mostrar opcionalmente el argumento solo si está presente, y ${var:-fallback}
para mostrar un nombre alternativo si no se proporciona explícitamente.
initialize_scala_modules() {
# ... Configuration variables and argument parsing ...
# 📦 Construct the project source paths
local scala_path
scala_path=$(get_package_path "src" "main" "scala" "${base_package[@]}")
local app_path="app/$scala_path/$(get_package_path "${app_package[@]}")"
local lib_path="lib/$scala_path/$(get_package_path "${lib_package[@]}")"
local app_file="$app_path/$app_file_name"
local lib_file="$lib_path/$lib_file_name"
# 🐛 Verbose diagnostics
$verbose && {
echo -e "${GREEN}📦 App: $app_file${RESET}"
echo -e "${GREEN}📦 Lib: $lib_file${RESET}"
}
# 🛠️ Create directories and files
new_scala_directory "$app_path" "app"
new_scala_directory "$lib_path" "lib"
new_scala_file "$app_file" "$app_file_name"
new_scala_file "$lib_file" "$lib_file_name"
}
Esta función arma toda la estructura de carpetas y archivos para un proyecto Scala modular.
- Utiliza
get_package_path
para construir la ruta base del código fuente ensrc/main/scala
. - Luego concatena los subpaquetes específicos de
app
ylib
, así como los nombres de archivo indicados. - Si está activado el modo
--verbose
, imprime las rutas generadas para que puedas ver qué se va a crear. - Finalmente llama a funciones auxiliares que crean los directorios y archivos, respetando los flags
--confirm
y--what-if
.
Esta es la función principal que traduce las configuraciones del usuario en acciones concretas sobre el sistema de archivos.
base_package=('com' 'github' 'username')
initialize_scala_modules \
--base-package "${base_package[@]}" \
--app-file-name 'App.scala' \
--lib-file-name 'Lib.scala' \
--verbose
🧱 Paso 2: Declarar los módulos del proyecto
Para transformar nuestro proyecto en una estructura multi-módulo, comenzamos por definir sus componentes principales dentro del archivo build.sbt
raíz:
val scala3Version = "3.6.4"
lazy val commonSettings = Seq(
scalaVersion := scala3Version
)
lazy val lib = project
.in(file("lib"))
.settings(commonSettings *)
lazy val app = project
.in(file("app"))
.dependsOn(lib)
.settings(commonSettings *)
En este paso transformamos nuestro proyecto en una estructura multi-módulo, lo que nos permite separar responsabilidades entre distintos componentes (por ejemplo, una biblioteca reutilizable y una aplicación principal).
- Primero definimos
scala3Version
como una variable para centralizar la versión del compilador. - Luego creamos una lista llamada
commonSettings
que contiene configuraciones compartidas, comoscalaVersion
. - A continuación declaramos dos módulos (
lib
yapp
) usandolazy val
:lib
se encuentra en el subdirectoriolib/
y recibe las configuraciones comunes.app
se encuentra enapp/
, también hereda las configuraciones comunes y depende explícitamente delib
usando.dependsOn(lib)
.
Usamos lazy val
porque sbt
necesita construir la estructura del proyecto de forma perezosa (lazy): permite que las referencias entre proyectos (como app.dependsOn(lib)
) se resuelvan sin problemas incluso si aún no se han evaluado por completo.
Esto evita errores de orden de inicialización y permite que sbt
maneje correctamente las dependencias entre módulos.
Esta estructura modular es especialmente útil en proyectos de bibliotecas, ya que permite mantener el código reutilizable separado del código específico de una aplicación o herramienta.
📦 Paso 3: Crear el módulo de biblioteca
Una vez declarado el subproyecto lib
, es momento de comenzar a escribir la lógica de negocio que deseamos reutilizar. Empezaremos con una función sencilla, pensada para ser consumida desde otros módulos:
package com.github.username
package echo
def echoMessage(message: String): String = message
Este archivo define un componente reutilizable dentro del subproyecto lib
.
- El paquete
com.github.username.echo
sigue la convención de dominios invertidos, facilitando la organización del código en proyectos más grandes. - La función
echoMessage
simplemente devuelve el mismo mensaje que recibe. Aunque su comportamiento es sencillo, nos servirá para verificar que otros módulos pueden importar y utilizar funcionalidades definidas en esta biblioteca.
Este módulo marca el punto de partida para construir una biblioteca bien estructurada, que podrá crecer y evolucionar conforme avancemos en el curso.
🚀 Paso 4: Crear el módulo de aplicación
Ahora que tenemos una biblioteca reutilizable en lib
, es momento de crear el subproyecto app
, encargado de ejecutar la lógica principal del programa. Esta aplicación imprimirá en consola los mensajes recibidos como argumentos, utilizando la función echoMessage
definida previamente.
package com.github.username
package echo
@main def app(args: String*): Unit =
for arg <- args do
println(echoMessage(arg))
Este archivo define la aplicación principal del proyecto. Su objetivo es utilizar la funcionalidad proporcionada por la biblioteca lib
.
- La anotación
@main
indica que esta es la función de entrada del programa. Scala 3 permite definir puntos de entrada sin necesidad de declarar una clase oobject
. - La función recibe los argumentos de línea de comandos como una secuencia variable (
String*
) y los recorre con un buclefor
. - Cada argumento se imprime utilizando la función
echoMessage
, definida en el subproyectolib
.
Gracias a esta integración, podemos verificar que app
depende correctamente de lib
, y que los módulos se comunican de forma efectiva dentro del mismo proyecto multi-módulo.
Este paso demuestra cómo separar la lógica de ejecución (aplicación) de la lógica reutilizable (biblioteca), una práctica esencial para construir proyectos bien organizados y escalables.
🧪 Paso 5: Ejecutar la aplicación
Con ambos módulos ya configurados y conectados, es momento de ejecutar app
desde la raíz del proyecto para comprobar que la integración entre módulos funciona correctamente.
sbt "app/run Alex Dim Nah Dim"
Deberías ver una salida como esta:
Alex
Dim
Nah
Dim
En este paso usamos el comando sbt "app/run"
para compilar y ejecutar el subproyecto app
.
Los argumentos que siguen (Alex Dim Nah Dim
) se envían directamente a la función @main
definida en App.scala
.
- Scala ejecuta la función principal con los argumentos indicados.
- Cada uno se procesa mediante
echoMessage
, definida en el subproyectolib
, y se imprime por separado. - Elegimos nombres de personajes de A Clockwork Orange como una forma divertida de verificar el comportamiento del programa.
Este paso valida que la estructura modular del proyecto está funcionando: app
puede usar sin problemas la lógica definida en lib
, y sbt maneja correctamente la compilación y ejecución de ambos módulos.
Con esta ejecución completamos la primera prueba de integración de nuestro proyecto multi-módulo. A partir de aquí, podemos escalar la aplicación o la biblioteca de forma independiente, manteniendo una separación clara de responsabilidades.
🎯 Conclusiones
Dividir un proyecto en múltiples módulos no solo mejora la organización del código, sino que sienta las bases para desarrollar software más escalable, reutilizable y mantenible. En esta lección aprendimos a estructurar un proyecto multi-módulo con sbt, separando la lógica principal de la aplicación (app
) de una biblioteca reutilizable (lib
), todo dentro de una configuración común y coherente.
También exploramos cómo ejecutar la aplicación con argumentos personalizados, lo que nos permitió validar la integración entre módulos y ver en acción una arquitectura modular.
🔑 Puntos clave
- sbt permite declarar múltiples subproyectos con
lazy val
dentro de un únicobuild.sbt
. - Compartir
commonSettings
asegura coherencia entre módulos y simplifica la configuración global. - La directiva
.dependsOn(...)
conecta módulos para compartir funcionalidades de forma explícita. - Scala 3 permite usar
@main
para definir puntos de entrada sin necesidad deobject
oApp
. - Para ejecutar un subproyecto, usamos
sbt "nombreModulo/run"
desde la raíz del proyecto.
🧰 ¿Qué nos llevamos?
Esta lección no solo mostró cómo configurar un proyecto multi-módulo, sino por qué conviene hacerlo desde el inicio.
Aprendimos a separar responsabilidades, reducir el acoplamiento y preparar la base del proyecto para escalar con claridad.
Con esta estructura podemos:
- Añadir nuevas funcionalidades en
lib
sin afectar directamente aapp
. - Reutilizar la biblioteca en otros proyectos si es necesario.
- Incorporar más módulos (como pruebas, documentación o herramientas internas) sin perder orden ni coherencia.
Empezamos con un simple "echo" y terminamos con una arquitectura preparada para crecer. En el desarrollo de bibliotecas, modularizar desde el principio es una decisión clave para crear proyectos sostenibles y profesionales.
📖 Referencias
🔥 Recomendadas
- 🌐 "Multi-project builds" en la documentación oficial de sbt: Introduce cómo estructurar múltiples subproyectos en un mismo
build.sbt
. Explica cómo definir módulos (lazy val
), compartir configuraciones y establecer dependencias con.dependsOn(...)
. Es clave para entender la arquitectura modular en sbt.
🔹 Adicionales
- 🎥 "SBT in Scala (part 2) - Setting up Modules, Organizing Builds, Using Plugins" (19m40s) en YouTube por Rock the JVM: Muestra paso a paso cómo crear un proyecto multi-módulo en sbt y aplicar buenas prácticas como centralizar configuraciones, usar plugins y definir constantes en archivos Scala. Ideal para complementar la teoría con un caso práctico.