8 - The Design of AppliAR.jl



First, let's look at some terms and definitions. We'll convert the procedure Invoicing into an activity diagram. Using the Onion Architecture pattern, we define the Domain objects and the Julia API-functions. In the Infrastructure layer, we put the functions that interact with the outer world and the inner layers.

Terms and Definitions

The points of attention are:

  • Procedure,
  • Domain-driven design,
  • Distributed processing, and
  • Style conventions.


A procedure is a description of work practice, a workflow. It describes a series of activities or actions in a particular order and interacts with people and machines. Actions make use of resources. Data, a service or a product, is the output of work.

Domain-Driven Design

Each process should be domain-specific. Subject matter experts and users of the domain speak the same language and use the same definitions and synonyms for concepts and objects. It leads to a Domain-driven design paradigm.

The Onion Architecture lends itself perfectly to the domain-driven design pattern. It divides an application into four areas: core, domain, API, and infrastructure.

The core consists out of the Julia language constructs and Julia modules. Modules are also called packages.

The next layer, the domain, defines the domain entities and concepts. Between its elements, there must be coherence. You only use constructs from the core. UnpaidInvoice is an example.

The next peel is the API. The API consists of Julia functions that operate on the domain elements, and are used to create programs. You only use constructs from the core and the domain.

create_unpaidinvoice, create_paidinvoice, create_pdf are examples.

The infrastructure layer is the ultimate peel. With its functions, it communicates with the external world. Adapters overcome mismatches between interfaces. When you write you the code, you use elements from the inner layers.

Distributed Processing

Programs, written in Julia language, also can run on other processor cores. Even in Docker containers on remote machines. Julia uses the master-worker concept. It means that the master executes Julia's functions on workers.

Style Conventions

The article Blue: a Style Guide for Julia describes the style conventions.

A Procedure as a Starting Point

In 1994 we were delivering Lotus Notes instructor-led training in the Netherlands. We became ISO-9001 certified one year later. ISO is short for the International Organization for Standardization. A part of ISO is the section procedures.

A procedure describes a workflow of a business process. It specifies the activities to be carried out by people or machines and the resources that are required to produce a result.

An input triggers a process. Every action creates an output, most of the time, modified information or side-effects such as saving data.

The example I use in the course is the procedure Invoicing.

The Course Example

In 1998 we rewrote our procedures as a table. Every row represents an activity or action. Next to the events are the columns with the roles involved with the work. The original procedure:

Procedure: Invoicing.

Roles: OM = Office Manage, AOM = Assistant Office Manager.

Input: List of orders.

1Create an invoice per orderRACreated and authorized invoicesOrder file
2Archive a copy of the invoiceRArchived copyAccounts Receivable unpaid
3Send the invoice to the customerRIInvoice sent
4Book the invoiceRABooked invoiceGeneral ledger
5Book the paid invoiceRAPaid invoiceBank records, General ledger
6Archive the paid invoiceRIArchived invoiceAccounts Receivable paid
7Check unpaid invoicesRList of unpaid invoices to contact customerNote in CRM system


  • R = Responsible, the entity who is responsible for the execution of the activity.
  • A = Approves, the entity who approves the result before going to the next step.
  • S = Supports, the members of the team.
  • C = Consults, an entity.
  • I = Informed, notify the entity about the result.

Let's see how we can automate the procedure with Julia. We tackle it with a technique of Domain-Driven Design and the Onion architecture.

The Procedure as an Activity Diagram

The activity diagram represents the workflow. The actions are Julia functions. You can add typed arguments and return values in Julia, noted by a double colon (::) followed by the name of the type. This defines the domain entities, e.g., ::Order, ::UnpaidInvoice.

⚉ process(::Array{Order)::Array{JournalEntry}
    send_email(::Array{UnpaidInvoice}) # TODO
    return ::Array{JournalEntry}

⚉ process(::Array{UnpaidInvoice}, ::Array{BankStatement})::Array{JournalEntry}
    filter(::Array{UnpaidInvoice}, ::Array{Bankstatement})::Array{PaidInvoice}
    return ::Array{JournalEntry}

⚉ report(::Array{UnpaidInvoice}, days::Int) # TODO
    filter(::Array{UnpaidInvoice, ::Int}::Array{UnpaidInvoiceDue}
    return ::Array{UnpaidInvoiceDue}

The Design

From the activity diagram we get:

Domain Elements

The domain objects (types) are:

Domain Types:

  • UnpaidInvoice;
  • PaidInvoice;
  • BankStatement.

External Types:

  • AppliSales.Order¹;
  • AppliGeneralLedger.JournalEntry².

General packages:

  • Dates³;
  • DataFrames³

¹ Defined in the package AppliSales.

² Defined in the package AppliGeneralLedger.

³ Dates is a sub-module of Julia. A package for manipulating data is DataFrames. The DataFrame data structure is comparable to a spreadsheet.


The API contains the methods (functions) of the module. The methods use only elements from the core or domain. An overview of we need:

  • create(::Array{Order,1})::Array{UnpaidInvoice, 1}
  • create(::Array{UnpaidInvoice, 1}, ::Array{BankStatement, 1})::Array{PaidInvoice,1)
  • conv2entry(inv::Array{UnpaidInvoice, 1}, from::Int, to::Int)::Array{JournalEntry, 1}
  • conv2entry(inv::Array{PaidInvoice, 1}, from::Int, to::Int)::Array{JournalEntry, 1}

In Julia, you can use the same function name as long as the signature is different, so other types and, or the number of arguments. One calls it multiple dispatch.

An Order, consists of the data objects Training, and Company plus additional data about contact, and student. Order is defined in domain.jl of the support-package AppliSales.

Also, we have already created a support-package AppliGeneralLedger to make it easier to test AppliAR.jl. JournalEntry is defined in domain.jl.

Methods of the Infrastructure Layer

Database, private methods:

  • add_to_file(file::String, data::Array{Any, 1}
  • read_from_file(file::String)::Array{Any, 1}

External accessible methods:

  • read_bank_statements(path::String)::Array{BankStatement,1}
  • process(::Array{Order)::Array{JounalEntry, 1}
  • process(::UnpaidInvoice, ::BankStatement)::Array{JounalEntry, 1}
  • retrieve_unpaid_invoices()::Array{UnpaidInvoice, 1}
  • retrieve_paid_invoice()::Array{PaidInvoice, 1}


  • I am thinking of Literate.jl as a package to make PDFs.
  • How to attach a PDF to an email?
  • How to send an email?
  • SMTPClient.jl