Organize Ballerina code

The sections below include information about packages and how you can manage the growth of your source code.

Package structure

Writing code in an organized manner from the beginning of the project is important for the lifecycle of the project and its maintainability in the long run. Organized code will make it easy to extend and improve your project over time. Ballerina project structure makes it easy to write clean code by eliminating repetitions, writing reusable code, adding new features without changing the existing code, etc. To achieve this, Ballerina has the concept of packages and modules.

Ballerina code is organized in a single shareable unit called a package.

A package is a collection of modules, and a module is a collection of Ballerina source files and test files. A package should contain at least one module, which becomes the default module. Each module has its own directory, which organizes source files and test files. A package can include resources in a resources directory located at the package source root, and test-specific resources in a resources directory within the tests directory at the package source root.

It is common in small projects to have only one (default) module in a package. As a result, the default module’s content is placed directly in the root of the package directory.

Create your first Ballerina package

The bal new command below creates a package with the default module.

Copy
$ bal new hello_world

This creates a new Ballerina package in the default module with the Ballerina.toml file, which identifies a directory as a package and a sample source file (i.e., main.bal) with a main function.

The bal new command generates the following file structure.

cd hello_world
tree .
.
├── Ballerina.toml
└── main.bal
    
0 directories, 2 files

Info: You may also create a service or a library package instead of the main function as explained in the sections below.

Create a Ballerina service package

Copy
$ bal new -t service hello_service

This creates a Ballerina source containing a service declaration with Ballerina tests to test the service. It creates the following file structure with the service template.

cd hello_service
.
├── Ballerina.toml
├── service.bal
└── tests
    └── service_test.bal

1 directory, 3 files

Create a library package

Copy
$ bal new -t lib hello_lib

This creates a Ballerina source file containing a function that prints Hello, world! along with a test file to test the function. Additionally, it creates the Package.md file, which is required to publish a package to Ballerina Central.


├── Ballerina.toml
├── hello_lib.bal
├── Module.md
├── Package.md
└── tests
    └── lib_test.bal

The default module

When a package is created with the bal new command, the Ballerina.toml and the main.bal files are created.

The main.bal file is a Ballerina source file, which belongs to the default module. The root directory of the default module is the root directory of the package as well. Therefore, the package root directory contains files that belong to the package as well as the default module.

You can add more source files at the package root, and all the top-level symbols (i.e., functions, variables, etc.) defined in one file will also be visible to other files as they share the same namespace. This namespace is called the default module of the package. The package name, which is specified in the Ballerina.toml file is also used to refer to the default module.

Non-default modules

As projects grow in complexity, the need arises to organize code better. This could be because you want to separate the functionalities of the package and/or to add boundaries to the visibility of certain functionalities. Therefore, Ballerina allows subdividing the code into multiple modules as well.

You can add more modules to the package using the bal add command:

Copy
$ cd hello_world
$ bal add util

This will create the modules directory in the package root. The modules/util directory is the root of the hello_world.util module. The package structure after adding a non-default module will have the directory structure below.

.
├── Ballerina.toml
├── main.bal
└── modules
    └── util
        ├── Module.md
        ├── tests
        │   └── lib_test.bal
        └── util.bal

4 directories, 5 files

Import a module from the same package

You can access any public symbol from another module of the package by importing the particular module using an import declaration.

The import declaration syntax is as follows.

Copy
import module_name [as import_prefix];

In the above syntax:

  • the import_prefix has to be a valid Ballerina identifier and the import_prefix is used to refer to public symbols in the declared module.
  • the import_prefix is optional. If it is not available, the last part of the module name can be used.

In a package, which has the default module containing the main.bal file and a non-default module named hello-world.util, you can add a public function in the hello_world.util module and use this function in the main.bal file in the default module.

Copy
import hello_world.util;

String formattedMsg = util:properCaseMessage("hello world!");

Since the import-prefix is not given, the module name util is used to refer to the symbols in the hello_world.util module.

Package references

For information on the structure of a package directory, see Package references.