3. Create and Test Domain.jl

To get experience with Julia and modules, we will build an application where we can register and retrieve persons.

You start with creating the sub-module file Domain.jl. Then you define in Domain.jl:

  • the objects Address and Person.
  • the enumerator AddressType with the values EMAIL and WORK.
  • the local function create_key, which will create a unique key based on the time of creation and address.
  • the objects that are accessible by other modules.

Finally, you define the test code in the file runtests.jl.

Contents

Domain.jl - Domain Objects

On the Domain page, you define the custom data structures that make-up your domain.

To define a data structure and type, use the keyword struct. The body consists of the fields of the data structure. A struct is a non-mutable object unless you precede it with the keyword mutable.

Use constructors to define standard values. It simplifies the creating of an object.

module Domain #1

using Dates #2

export Person, Address, AddressType, EMAIL, WORK #3

# local function to generate an unique id
create_key(name::String) = string(hash(name * string(time()))) #4

# enumerated type for an address.
@enum AddressType EMAIL WORK #5

struct Address #6
  id::String
  created::DateTime
  address_type::AddressType
  address::String
  #constructors
  Address(address_type, address) = new(create_key(address), now(), address_type, address)
end # Address

struct Person #7
  id::String
  created::DateTime
  name::String
  addresses::Array{Address, 1}
  #constructors
  Person(name) = new(create_key(name), now(), name, [])
  Person(name, addresses) = new(create_key(name), now(), name, addresses)
end # Person

end

#1 Module names start with a capital letter. See the Blue: a Style Guide for Julia.

#2 If you need date functions like time(), date(), or now() you have to load the Dates package.

#3 With the keyword export you define what elements are default available to users.

#4 We use the hash function to generate a unique id. You use * to concatenate Strings in Julia. The function string converts the date-time value to a String value.

#5 The AddressTypes that you allow in an Address object.

#6 The structure of the Address datatype. The constructor Address(address_type, address) allows the user to only specify the AddressType and the address. The values for the fields id and created are generated by the Julia code.

#7 The Person datatype. The values for the fields id and created are generated by Julia code. When you don't specify an address, the software creates an empty array. Later on, you can add addresses using the push! function.

Activity 3.1: Create the File Domain.jl in the src-Folder

One of the parts of the Onion architecture is the Domain, materialized in the file Domain.jl. The Domain only communicates with Julia or Julia modules or packages.

Prerequisites (activities of chapter 2)
  1. Activity 2.1: Setup the Development Environment.
  2. Activity 2.2: Create the Accounts module.
  3. Activity 2.3: Create a Repository on GitHub.

In this activity you will:

  • Create the file Domain.jl.

StepActionComment
1cd ~/.julia/dev/AccountsGo to the .julia/dev/Accounts folder
2$ code .Start VSCode.
3Right click on: src
4Select: New file
5Type: Domain.jlA file that represents a module starts with a capital letter.
6Press: <Enter>A new document appears in the pane next to the navigation pane.

In the navigation pane you see the next files in the folder src:

v Accounts
  v src
    - Accounts.jl
    - Domain.jl

Activity 3.2: Pasting the Domain Code into Domain.jl

We define the Domain as a Julia sub-module. In a module, you declare the export and import items.

Export items are items that are used by other modules and Julia programs. An item can be an object (struct) or a function.

Import items are the items that you use from other modules like the Dates module of Julia. Later on, you will learn how to declare this kind of dependencies in the file Project.toml.

If you don't change the names and their types, you could consider the module as an interface. The advantage is that you can change the implementation of an item without affecting other external parts.

Prerequisites

In this activity you will:

  • Add the Domain example code to the file Domain.jl.
  • Save the file.

StepActionComment
1$ cd ~/.julia/dev/Accounts
2$ code .Open VSCode.
3Select the code from the section Domain.jl-Domain-objects
4Ctrl-CCopy the code to the clipboard.
5Paste the code in the file Domain.jl
6Ctrl-SSave the file.
7Close VSCodeOr continue with Activity 3.3 step 3.

Activity 3.3: Update Accounts.jl

The sub-modules that you create are listed and instantiated in the main-module Accounts.jl.

Prerequisites

In this activity you will:

  • Declare Domain as a sub-module of Accounts in Accounts.jl.

StepActionComment
1$ cd ~/.julia/dev/Accounts
2$ code .Open VSCode.
3Open file: Accounts.jl
4Delete all lines
5Replace the code into the file with the fo;;owimg code:
module Accounts

include("Domain.jl"); using .Domain #1

end

#1 the function include loads the code of the specified file. using .Domain activates the module. The dot refers to a sub-module of Accounts. The ; separates the two Julia statements.

StepActionComment
6Ctrl-SSave the file.
7Close VSCodeOr continue with Activity 3.4 step 3.

Dependencies

You declare all dependencies of your module in the file Project.toml under the section [deps]. The now()-function belongs to Dates module. Dates is a Julia module and you load with using Dates.

name = "Accounts"
uuid = "a1b4bf14-7ec5-4e42-8992-fb1d0e08b0e4"
authors = ["Rob Bontekoe <rbontekoe@appligate.nl> and contributors"]
version = "0.1.0"

[deps]
Dates = "ade2ca70-3891-5945-98fb-dc099432e06a"

[compat]
julia = "1"

[extras]
Test = "8dfed614-e22c-5e08-85e1-65c5234f0b40"

[targets]
test = ["Test"]

Activity 3.4: Adding Dates as Dependency

You define dependencies on other packages in the file Project.toml. If you have activated the Accounts environment (Pkg.activate(".") then when you add a package, Julia will update Project.toml for you. If the file Project.toml does not exist, Julia will create it.

Prerequisites

In this activity you will:

  • Activate the Accounts environment.
  • Add the dependency Dates to the environment.
  • Inspect the status of the active environment.

StepActionComment
1$ cd ~/.julia/dev/Accounts
2$ code .Start VSCode.
1Crt+Shift-PShow All Commands.
2Select: Julia: start REPLOpen REPL.
3jullia> ]Type ']' to activate the package manager.
4pkg> activate .Activate the Accounts environment.
5(Accounts) pkg> statusShow Accounts loaded packages (dependencies)
6(Accounts) pkg> add DatesAdd the Dates module.
7(Accounts) pkg> stShow your dependencies. You can abbreviate your commands. Use the arrow-up button to retrieve previous commands.
Project Accounts v0.1.0
Status `~/.julia/dev/Accounts/Project.toml`
  [ade2ca70] Dates
StepActionComment
6Press: <BackSpace>Return to the Julia prompt.
7Close VSCodeOr continue with Activity 3.5 step 3.

Activity 3.5: Testing your Code

One way to test your model is to put the test code in a file and execute the code line by line.

Prerequisites

In this acticity you will:

  • Create the file test_domain.jl.
  • Save the file.
  • Execute the code line by line.

StepActionComment
1$ cd ~/.julia/dev/Accounts
2$ code .Start VSCode.
3Right click on: src
4Select: New file
5Type: test_domain.jl
6Press: <Enter>Create the file.
7Copy the following example code into the file:
using Pkg; Pkg.activate(".")

import Accounts: Domain

using .Domain

donald_email = Address(EMAIL, "donald@duckcity.com")
donald_work = Address(WORK,
  """
  Donalds Hardware Store
  attn. Donald Duck
  1190 Seven Seas Dr
  FL 32830 Lake Buena Vista
  USA
  """
)

addresses = [donald_email, donald_work]

donald = Person("Donald Duck", addresses)

email_address = filter(x -> x.address_type == EMAIL, donald.addresses)

@info email_address[1].address
StepActionComment
8Ctrl-SSave the file.
9Select the first line
10Alt-EnterThe line will execute.
11Repeat until the last line.
12Close VSCodeOr continue with Activity 3.6 step 3.
Info

When you hover over the result (to the right of the blue separator), you will see it in a popup.

Hovering over any variable will show its value.

Activity 3.6: runtests.jl

It is even better to put you test code in the file runtests.jl. The folder test contains the file.

Prerequisites

In this activity you will:

  • Add unit test code to the file runtests.jl.
  • Test the module Accounts.

StepActionComment
1$ cd ~/.julia/dev/Accounts
2$ code.Start VSCode.
3Select the folder: test
v Accounts
   > src
  v test
    - runtests.jl
StepActionComment
4Open the file: runtests.jl.
5Copy and paste the following code in runtests.jlSee: runtests.jl
using Accounts
using Test

import Accounts: Domain
using .Domain

@testset "Domain.jl" begin
    donald_email = Address(EMAIL, "donald@duckcity.com")
    donald = Person("Donald duck", [donald_email])
    email_addresses = filter(x -> x.address_type == EMAIL, donald.addresses)
    @test email_addresses[1].address == "donald@duckcity.com"
end
StepActionComment
5Ctrl-SSave the file.
7julia> ]Activate the package manager.
8pkg> activate .Activate the Accounts environment.
9(Accounts) Pkg> stCheck whether Dates is loaded. See Activity 3.4 - Adding Dates as Dependency.
10(Accounts) Pkg> test AccountsRun the test code. The result is:
Test Summary: | Pass  Total
Domain.jl     |    1      1
    Testing Accounts tests passed
StepActionComment
11Close VSCodeOr continue with Activity 3.7 step 3.

Activity 3.7: Update your GitHub Repository

It is time to push our changes to the GitHub repository. VSCode supports Git.

Prerequisites

In this activity you will:

  • Open the Git-pane.
  • Add a message.
  • Stage your changes.
  • Commit your changes.
  • Push your changes to GitHub repository Accounts.jl.

StepActionComment
1Shift+Ctrl-GOpen the Git-pane when it is not visible.
2Place your cursor in the field Messages
3Type: Add Domain.jl sub-module
4In the line Changes, click on symbol: +Stage all your changes.
5At the top of the pane, click on: ✓Click on the checkmark ✓ (Commit) at the top.
6Click on: ∙∙∙Open Views and More Actions menu. It is located in the upper right corner of the pane.
7Select: PushPush your changes to GitHub.

You see a list of changed (ocher) and new (green) files (Domain.jl and test_domain.jl) in the section Staged Changes of the pane.

Project.toml
src/Accounts.jl
src/Domain.jl
src/test_domain.jl
test/runtests.jl
StepActionComment
5Click on: Commit to master
6Click on the Push buttonThe Push-button is located at the lower right corner.
7Go to GitHub and check whether your changes are visible
8Close VSCode

Summary

We apply the Onion Architecture to build our model that consists of the layers Domain, API, and Infrastructure. We define them as Julia sub-modules. We can modify the implementation of a module without dramatic consequences for other program code.

A module is a block of code between the keywords module <name module> and end. The module name starts with a capital letter. Also, the name of the file begins with an uppercase letter.

We start with the Domain. The code consists of self-defined data structures, local functions, and enumerations of values, which you want to allow in the module.

The Domain only communicates with Julia and packages.

An example of a data structure in the course is the type Address. Here we mention the fields names with their data type. The constructors determine how we can create the data type.

We also defined the fields id and created. The hash function determines the unique value of the id based on the combination of address and time. It plays a role in CQRS and Event Sourcing. CQRS stands for Command Query Responsibility Segregation.

We could use the field created for data analysis in the future.

Unit code is placed in the file runtests.jl in the folder test.

Dependency on other modules or packages is in the file Project.toml.

Shortcuts for VSCode
StepActionCommand
Ctrl+Shift-PShow All Commands.
Ctrl+Shift-GOpen Git Control Source.
Ctrl+Shift-EOpen Explorer.

You can also activate the commands from the icon bar in the most left column.