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, test files, and resources. A package should contain at least one module called the default module. Each module has its own directory, which organizes source files, test files, and resources.

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.

The bal new command creates a package with the default module. This will generate the Ballerina.toml file, which identifies a directory as a package and will additionally generate a sample source file in the default module.

Create your first Ballerina package

Create a Ballerina package with the bal new command as follows.

bal new hello_world

This will create a new Ballerina package 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

You may also try creating a service or a library package instead of the main function as explained in the sections below.

Create a Ballerina service package

bal new -t service hello_service

This will create a Ballerina source containing a service declaration with Ballerina tests to test the service. The following file structure will be generated with the service template.

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

1 directory, 3 files

Create a library package

bal new -t lib hello_lib

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


├── Ballerina.toml
├── hello_lib.bal
├── Module.md
├── Package.md
├── resources
└── 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:

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
        ├── resources
        ├── 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.

import module-name [as import-prefix];
  • 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.

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.