рд╕реЙрдлреНрдЯрд╡реЗрдпрд░ рд╡рд┐рдХрд╛рд╕ рдореЗрдВ рд▓рдбрд╝рд╛рдИ рдХреА рдЬрдЯрд┐рд▓рддрд╛

рдпрд╣ рдХрд┐рд╕ рдмрд╛рд░реЗ рдореЗрдВ рд╣реИ


рд╡рд┐рднрд┐рдиреНрди рдкрд░рд┐рдпреЛрдЬрдирд╛рдУрдВ рдкрд░ рдХрд╛рдо рдХрд░рдиреЗ рдХреЗ рдмрд╛рдж, рдореИрдВрдиреЗ рджреЗрдЦрд╛ рд╣реИ рдХрд┐ рдЙрдирдореЗрдВ рд╕реЗ рд╣рд░ рдПрдХ рдХреЛ рдбреЛрдореЗрди, рд╡рд╛рд╕реНрддреБрдХрд▓рд╛, рдХреЛрдб рд╕рдореНрдореЗрд▓рди рдФрд░ рдЗрддрдиреЗ рдкрд░, рдХреБрдЫ рд╕рд╛рдорд╛рдиреНрдп рд╕рдорд╕реНрдпрд╛рдПрдВ рдереАрдВред рдЙрди рд╕рдорд╕реНрдпрд╛рдУрдВ рдХреЛ рдЪреБрдиреМрддреА рдирд╣реАрдВ рдереА, рдмрд╕ рдПрдХ рдердХрд╛рдК рджрд┐рдирдЪрд░реНрдпрд╛: рд╕реБрдирд┐рд╢реНрдЪрд┐рдд рдХрд░реЗрдВ рдХрд┐ рдЖрдк рдХреБрдЫ рднреА рдмреЗрд╡рдХреВрдл рдФрд░ рд╕реНрдкрд╖реНрдЯ рдпрд╛рдж рдирд╣реАрдВ рдХрд┐рдпрд╛ред рджреИрдирд┐рдХ рдЖрдзрд╛рд░ рдкрд░ рдЗрд╕ рджрд┐рдирдЪрд░реНрдпрд╛ рдХреЛ рдХрд░рдиреЗ рдХреЗ рдмрдЬрд╛рдп, рдореИрдВ рд╕рдорд╛рдзрд╛рди рдХреА рддрд▓рд╛рд╢ рдореЗрдВ рдЬреБрдиреВрдиреА рд╣реЛ рдЧрдпрд╛: рдХреБрдЫ рд╡рд┐рдХрд╛рд╕ рджреГрд╖реНрдЯрд┐рдХреЛрдг рдпрд╛ рдХреЛрдб рдХрдиреНрд╡реЗрдВрд╢рди рдпрд╛ рдЬреЛ рдХреБрдЫ рднреА рдореБрдЭреЗ рдЗрд╕ рддрд░рд╣ рд╕реЗ рдПрдХ рдкреНрд░реЛрдЬреЗрдХреНрдЯ рдбрд┐рдЬрд╛рдЗрди рдХрд░рдиреЗ рдореЗрдВ рдорджрдж рдХрд░реЗрдЧрд╛ рдЬреЛ рдЙрди рд╕рдорд╕реНрдпрд╛рдУрдВ рдХреЛ рд╣реЛрдиреЗ рд╕реЗ рд░реЛрдХреЗрдЧрд╛, рдЗрд╕рд▓рд┐рдП рдореИрдВ рджрд┐рд▓рдЪрд╕реНрдк рд╕рд╛рдордЧреНрд░реА рдкрд░ рдзреНрдпрд╛рди рдХреЗрдВрджреНрд░рд┐рдд рдХрд░ рд╕рдХрддрд╛ рд╣реВрдВ ред рдпрд╣ рдЗрд╕ рд▓реЗрдЦ рдХрд╛ рд▓рдХреНрд╖реНрдп рд╣реИ: рдЙрди рд╕рдорд╕реНрдпрд╛рдУрдВ рдХрд╛ рд╡рд░реНрдгрди рдХрд░рдирд╛ рдФрд░ рдЖрдкрдХреЛ рдЙрди рдЙрдкрдХрд░рдгреЛрдВ рдФрд░ рджреГрд╖реНрдЯрд┐рдХреЛрдгреЛрдВ рдХрд╛ рдорд┐рд╢реНрд░рдг рджрд┐рдЦрд╛рдирд╛ рдЬреЛ рдореИрдВрдиреЗ рдЙрдиреНрд╣реЗрдВ рд╣рд▓ рдХрд░рдиреЗ рдХреЗ рд▓рд┐рдП рдкрд╛рдпрд╛ред


рд╕рдорд╕реНрдпрд╛рдУрдВ рдХрд╛ рд╣рдо рд╕рд╛рдордирд╛ рдХрд░рддреЗ рд╣реИрдВ


рд╕реЙрдлрд╝реНрдЯрд╡реЗрдпрд░ рд╡рд┐рдХрд╕рд┐рдд рдХрд░рддреЗ рд╕рдордп рд╣рдо рд░рд╛рд╕реНрддреЗ рдореЗрдВ рдмрд╣реБрдд рд╕реА рдХрдард┐рдирд╛рдЗрдпреЛрдВ рдХрд╛ рд╕рд╛рдордирд╛ рдХрд░рддреЗ рд╣реИрдВ: рдЕрд╕реНрдкрд╖реНрдЯ рдЖрд╡рд╢реНрдпрдХрддрд╛рдПрдВ, рдЧрд▓рдд рд╕рдВрдЪрд╛рд░, рдЦрд░рд╛рдм рд╡рд┐рдХрд╛рд╕ рдкреНрд░рдХреНрд░рд┐рдпрд╛ рдФрд░ рдЗрддрдиреЗ рдкрд░ред


рд╣рдо рдХреБрдЫ рддрдХрдиреАрдХреА рдХрдард┐рдирд╛рдЗрдпреЛрдВ рдХрд╛ рднреА рд╕рд╛рдордирд╛ рдХрд░рддреЗ рд╣реИрдВ: рд╡рд┐рд░рд╛рд╕рдд рдХреЛрдб рд╣рдореЗрдВ рдзреАрдорд╛ рдХрд░ рджреЗрддрд╛ рд╣реИ, рд╕реНрдХреЗрд▓рд┐рдВрдЧ рдореБрд╢реНрдХрд┐рд▓ рд╣реИ, рдЕрддреАрдд рдХреЗ рдХреБрдЫ рдмреБрд░реЗ рдлреИрд╕рд▓реЗ рдЖрдЬ рд╣рдореЗрдВ рджрд╛рдВрддреЛрдВ рдореЗрдВ рдорд╛рд░рддреЗ рд╣реИрдВред


рдЙрдирдореЗрдВ рд╕реЗ рд╕рднреА рдХреЛ рд╕рдорд╛рдкреНрдд рдирд╣реАрдВ рдХрд┐рдпрд╛ рдЬрд╛ рд╕рдХрддрд╛ рд╣реИ рддреЛ рдХрд╛рдлреА рдХрдо рд╣реЛ рд╕рдХрддрд╛ рд╣реИ, рд▓реЗрдХрд┐рди рдПрдХ рдореМрд▓рд┐рдХ рд╕рдорд╕реНрдпрд╛ рд╣реИ рдЬрд┐рд╕рдХреЗ рдмрд╛рд░реЗ рдореЗрдВ рдЖрдк рдХреБрдЫ рдирд╣реАрдВ рдХрд░ рд╕рдХрддреЗ рд╣реИрдВ: рдЖрдкрдХреЗ рд╕рд┐рд╕реНрдЯрдо рдХреА рдЬрдЯрд┐рд▓рддрд╛ред


рдЖрдкрдХреЗ рджреНрд╡рд╛рд░рд╛ рд╡рд┐рдХрд╕рд┐рдд рдХреА рдЬрд╛ рд░рд╣реА рдкреНрд░рдгрд╛рд▓реА рдХрд╛ рд╡рд┐рдЪрд╛рд░ рд╣рдореЗрд╢рд╛ рдЬрдЯрд┐рд▓ рд╣реЛрддрд╛ рд╣реИ, рдЪрд╛рд╣реЗ рдЖрдк рдЗрд╕реЗ рд╕рдордЭреЗрдВ рдпрд╛ рдирд╣реАрдВред
рдпрд╣рд╛рдВ рддрдХ тАЛтАЛрдХрд┐ рдЬрдм рдЖрдк рдЕрднреА рддрдХ рдПрдХ рдФрд░ рд╕реАрдЖрд░рдпреВрдбреА рдЖрд╡реЗрджрди рдХрд░ рд░рд╣реЗ рд╣реИрдВ, рддреЛ рд╣рдореЗрд╢рд╛ рдХреБрдЫ рдХрд┐рдирд╛рд░реЗ рдорд╛рдорд▓реЗ, рдХреБрдЫ рдореБрд╢реНрдХрд┐рд▓ рдЪреАрдЬреЗрдВ рд╣реЛрддреА рд╣реИрдВ, рдФрд░ рд╕рдордп-рд╕рдордп рдкрд░ рдХреЛрдИ рд╡реНрдпрдХреНрддрд┐ рдкреВрдЫрддрд╛ рд╣реИ "рдЕрд░реЗ, рдХреНрдпрд╛ рд╣реЛрдЧрд╛ рдЕрдЧрд░ рдореИрдВ рдЗрди рдкрд░рд┐рд╕реНрдерд┐рддрд┐рдпреЛрдВ рдореЗрдВ рдРрд╕рд╛ рдХрд░реВрдВ рдФрд░ рдпрд╣ рдХрд░реВрдВ?" рдФрд░ рдЖрдк рдХрд╣рддреЗ рд╣реИрдВ, "рд╣рдореНрдо, рдпрд╣ рдПрдХ рдмрд╣реБрдд рдЕрдЪреНрдЫрд╛ рд╕рд╡рд╛рд▓ рд╣реИред"


рд╡реЗ рдкреЗрдЪреАрджрд╛ рдорд╛рдорд▓реЗ, рдЫрд╛рдпрд╛рджрд╛рд░ рддрд░реНрдХ, рдорд╛рдиреНрдпрддрд╛ рдФрд░ рдкрд╣реБрдВрдЪ рдкреНрд░рдмрдВрдзрди - рдпрд╣ рд╕рдм рдЖрдкрдХреЗ рдмрдбрд╝реЗ рд╡рд┐рдЪрд╛рд░ рдХреЛ рдЬреЛрдбрд╝рддрд╛ рд╣реИред
рдЕрдХреНрд╕рд░ рдпрд╣ рд╡рд┐рдЪрд╛рд░ рдЗрддрдирд╛ рдмрдбрд╝рд╛ рд╣реИ рдХрд┐ рдпрд╣ рдПрдХ рд╕рд┐рд░ рдореЗрдВ рдлрд┐рдЯ рдирд╣реАрдВ рд╣реЛрддрд╛ рд╣реИ, рдФрд░ рдпрд╣ рддрдереНрдп рдЕрдХреЗрд▓реЗ рдЧрд▓рддрдлрд╣рдореА рдЬреИрд╕реА рд╕рдорд╕реНрдпрд╛рдПрдВ рд▓рд╛рддрд╛ рд╣реИред


рд▓реЗрдХрд┐рди рдЖрдЗрдП рдЙрджрд╛рд░ рдмрдиреЗрдВ рдФрд░ рдорд╛рди рд▓реЗрдВ рдХрд┐ рдбреЛрдореЗрди рд╡рд┐рд╢реЗрд╖рдЬреНрдЮреЛрдВ рдФрд░ рд╡реНрдпрд╛рдкрд╛рд░ рд╡рд┐рд╢реНрд▓реЗрд╖рдХреЛрдВ рдХреА рдпрд╣ рдЯреАрдо рд╕реНрдкрд╖реНрдЯ рд░реВрдк рд╕реЗ рд╕рдВрдЪрд╛рд░ рдХрд░рддреА рд╣реИ рдФрд░ рдареАрдХ рд╕рдВрдЧрдд рдЖрд╡рд╢реНрдпрдХрддрд╛рдУрдВ рдХрд╛ рдЙрддреНрдкрд╛рджрди рдХрд░рддреА рд╣реИред


рдЕрдм рд╣рдореЗрдВ рдЕрдкрдиреЗ рдХреЛрдб рдореЗрдВ рдЙрд╕ рдЬрдЯрд┐рд▓ рд╡рд┐рдЪрд╛рд░ рдХреЛ рд╡реНрдпрдХреНрдд рдХрд░рдиреЗ рдХреЗ рд▓рд┐рдП, рдЙрдиреНрд╣реЗрдВ рд▓рд╛рдЧреВ рдХрд░рдирд╛ рд╣реЛрдЧрд╛ред рдЕрдм рд╡рд╣ рдХреЛрдб рдПрдХ рдЕрдиреНрдп рдкреНрд░рдгрд╛рд▓реА рд╣реИ, рдореВрд▓ рд╡рд┐рдЪрд╛рд░ рдХреА рддреБрд▓рдирд╛ рдореЗрдВ рдЕрдзрд┐рдХ рдЬрдЯрд┐рд▓ рд╣реИ рдЬреЛ рд╣рдорд╛рд░реЗ рдорди рдореЗрдВ рдерд╛ред


рдРрд╕рд╛ рдХреИрд╕реЗ? рдпрд╣ рд╡рд╛рд╕реНрддрд╡рд┐рдХрддрд╛ рдХрд╛ рд╕рд╛рдордирд╛ рдХрд░рддрд╛ рд╣реИ: рддрдХрдиреАрдХреА рд╕реАрдорд╛рдПрдВ рдЖрдкрдХреЛ рд╡рд╛рд╕реНрддрд╡рд┐рдХ рд╡реНрдпрд╡рд╕рд╛рдп рддрд░реНрдХ рдХреЛ рд▓рд╛рдЧреВ рдХрд░рдиреЗ рдХреЗ рд╢реАрд░реНрд╖ рдкрд░ рдЙрдЪреНрдЪ рднрд╛рд░, рдбреЗрдЯрд╛ рд╕реНрдерд┐рд░рддрд╛ рдФрд░ рдЙрдкрд▓рдмреНрдзрддрд╛ рд╕реЗ рдирд┐рдкрдЯрдиреЗ рдХреЗ рд▓рд┐рдП рдордЬрдмреВрд░ рдХрд░рддреА рд╣реИрдВред


рдЬреИрд╕рд╛ рдХрд┐ рдЖрдк рджреЗрдЦ рд╕рдХрддреЗ рд╣реИрдВ рдХрд┐ рдХрд╛рд░реНрдп рдмрд╣реБрдд рдЪреБрдиреМрддреАрдкреВрд░реНрдг рд╣реИ, рдФрд░ рдЕрдм рд╣рдореЗрдВ рдЗрд╕рд╕реЗ рдирд┐рдкрдЯрдиреЗ рдХреЗ рд▓рд┐рдП рдЙрдЪрд┐рдд рдЙрдкрдХрд░рдгреЛрдВ рдХреА рдЖрд╡рд╢реНрдпрдХрддрд╛ рд╣реИред
рдПрдХ рдкреНрд░реЛрдЧреНрд░рд╛рдорд┐рдВрдЧ рднрд╛рд╖рд╛ рд╕рд┐рд░реНрдл рдПрдХ рдФрд░ рдЙрдкрдХрд░рдг рд╣реИ, рдФрд░ рд╣рд░ рджреВрд╕рд░реЗ рдЙрдкрдХрд░рдг рдХреА рддрд░рд╣, рдпрд╣ рд╕рд┐рд░реНрдл рдЗрд╕рдХреА рдЧреБрдгрд╡рддреНрддрд╛ рдХреЗ рдмрд╛рд░реЗ рдореЗрдВ рдирд╣реАрдВ рд╣реИ, рдпрд╣ рд╕рдВрднрд╡рддрдГ рдиреМрдХрд░реА рдлрд┐рдЯрд┐рдВрдЧ рдХрд░рдиреЗ рд╡рд╛рд▓реЗ рдЙрдкрдХрд░рдг рдХреЗ рдмрд╛рд░реЗ рдореЗрдВ рдФрд░ рднреА рдЕрдзрд┐рдХ рд╣реИред рдЖрдкрдХреЗ рдкрд╛рд╕ рд╕рдмрд╕реЗ рдЕрдЪреНрдЫрд╛ рдкреЗрдЪрдХрд╢ рд╣реЛ рд╕рдХрддрд╛ рд╣реИ, рд▓реЗрдХрд┐рди рдЕрдЧрд░ рдЖрдкрдХреЛ рд▓рдХрдбрд╝реА рдореЗрдВ рдХреБрдЫ рдирд╛рдЦреВрди рд▓рдЧрд╛рдиреЗ рдХреА рдЬрд╝рд░реВрд░рдд рд╣реИ, рддреЛ рдПрдХ рднрджреНрджрд╛ рд╣рдереМрдбрд╝рд╛ рдмреЗрд╣рддрд░ рд╣реЛрдЧрд╛, рд╣реИ рдирд╛?


рддрдХрдиреАрдХреА рдкрд╣рд▓реВ


рдЕрдзрд┐рдХрд╛рдВрд╢ рд▓реЛрдХрдкреНрд░рд┐рдп рднрд╛рд╖рд╛рдПрдБ рдЖрдЬ рдПрдХ рд╡рд╕реНрддреБ рдЙрдиреНрдореБрдЦ рд╣реИрдВред рдЬрдм рдХреЛрдИ OOP рд╕реЗ рдкрд░рд┐рдЪрдп рдХрд░рддрд╛ рд╣реИ рддреЛ рд╡реЗ рдЖрдорддреМрд░ рдкрд░ рдЙрджрд╛рд╣рд░рдгреЛрдВ рдХрд╛ рдЙрдкрдпреЛрдЧ рдХрд░рддреЗ рд╣реИрдВ:
рдПрдХ рдХрд╛рд░ рдкрд░ рд╡рд┐рдЪрд╛рд░ рдХрд░реЗрдВ, рдЬреЛ рд╡рд╛рд╕реНрддрд╡рд┐рдХ рджреБрдирд┐рдпрд╛ рд╕реЗ рдПрдХ рд╡рд╕реНрддреБ рд╣реИред рдЗрд╕рдореЗрдВ рдмреНрд░рд╛рдВрдб, рд╡рдЬрди, рд░рдВрдЧ, рдЕрдзрд┐рдХрддрдо рдЧрддрд┐, рд╡рд░реНрддрдорд╛рди рдЧрддрд┐ рдФрд░ рдЗрд╕реА рддрд░рд╣ рдХреЗ рд╡рд┐рднрд┐рдиреНрди рдЧреБрдг рд╣реИрдВред


рд╣рдорд╛рд░реЗ рдХрд╛рд░реНрдпрдХреНрд░рдо рдореЗрдВ рдЗрд╕ рдСрдмреНрдЬреЗрдХреНрдЯ рдХреЛ рдкреНрд░рддрд┐рдмрд┐рдВрдмрд┐рдд рдХрд░рдиреЗ рдХреЗ рд▓рд┐рдП рд╣рдо рдЙрди рдЧреБрдгреЛрдВ рдХреЛ рдПрдХ рдХрдХреНрд╖рд╛ рдореЗрдВ рдЗрдХрдЯреНрдард╛ рдХрд░рддреЗ рд╣реИрдВред рдЧреБрдг рд╕реНрдерд╛рдпреА рдпрд╛ рдкрд░рд┐рд╡рд░реНрддрдирд╢реАрд▓ рд╣реЛ рд╕рдХрддреЗ рд╣реИрдВ, рдЬреЛ рдПрдХ рд╕рд╛рде рдЗрд╕ рд╡рд╕реНрддреБ рдХреА рд╡рд░реНрддрдорд╛рди рд╕реНрдерд┐рддрд┐ рдФрд░ рдХреБрдЫ рд╕реАрдорд╛рдУрдВ рдХреЛ рдмрдирд╛рддреЗ рд╣реИрдВ рдЬрд┐рд╕рдореЗрдВ рдпрд╣ рднрд┐рдиреНрди рд╣реЛ рд╕рдХрддреЗ рд╣реИрдВред рд╣рд╛рд▓рд╛рдБрдХрд┐ рдЙрди рдЧреБрдгреЛрдВ рдХрд╛ рд╕рдВрдпреЛрдЬрди рдкрд░реНрдпрд╛рдкреНрдд рдирд╣реАрдВ рд╣реИ, рдХреНрдпреЛрдВрдХрд┐ рд╣рдореЗрдВ рдпрд╣ рдЬрд╛рдБрдЪрдирд╛ рд╣реИ рдХрд┐ рд╡рд░реНрддрдорд╛рди рд╕реНрдерд┐рддрд┐ рд╕рдордЭ рдореЗрдВ рдЖрддреА рд╣реИ, рдЬреИрд╕реЗ рд╡рд░реНрддрдорд╛рди рдЧрддрд┐ рдЕрдзрд┐рдХрддрдо рдЧрддрд┐ рд╕реЗ рдЕрдзрд┐рдХ рдирд╣реАрдВ рд╣реИред рдпрд╣ рд╕реБрдирд┐рд╢реНрдЪрд┐рдд рдХрд░рдиреЗ рдХреЗ рд▓рд┐рдП рдХрд┐ рд╣рдо рдЗрд╕ рд╡рд░реНрдЧ рдХреЗ рд▓рд┐рдП рдХреБрдЫ рддрд░реНрдХ рджреЗрддреЗ рд╣реИрдВ, рдХрд┐рд╕реА рдХреЛ рдЕрд╡реИрдз рд░рд╛рдЬреНрдп рдмрдирд╛рдиреЗ рд╕реЗ рд░реЛрдХрдиреЗ рдХреЗ рд▓рд┐рдП рдЧреБрдгреЛрдВ рдХреЛ рдирд┐рдЬреА рдХреЗ рд░реВрдк рдореЗрдВ рдЪрд┐рд╣реНрдирд┐рдд рдХрд░реЗрдВред
рдЬреИрд╕рд╛ рдХрд┐ рдЖрдк рджреЗрдЦ рд╕рдХрддреЗ рд╣реИрдВ рдХрд┐ рд╡рд╕реНрддреБрдПрдВ рдЙрдирдХреА рдЖрдВрддрд░рд┐рдХ рд╕реНрдерд┐рддрд┐ рдФрд░ рдЬреАрд╡рди рдЪрдХреНрд░ рдХреЗ рдмрд╛рд░реЗ рдореЗрдВ рд╣реИрдВред


рддреЛ OOP рдХреЗ рд╡реЗ рддреАрди рд╕реНрддрдВрдн рдЗрд╕ рд╕рдВрджрд░реНрдн рдореЗрдВ рд╕рд╣реА рдЕрд░реНрде рд░рдЦрддреЗ рд╣реИрдВ: рд╣рдо рд╡рд┐рд░рд╛рд╕рдд рдХрд╛ рдЙрдкрдпреЛрдЧ рдХреБрдЫ рд░рд╛рдЬреНрдп рдЬреЛрдбрд╝рддреЛрдбрд╝реЛрдВ рдХрд╛ рдкреБрди: рдЙрдкрдпреЛрдЧ рдХрд░рдиреЗ рдХреЗ рд▓рд┐рдП рдХрд░рддреЗ рд╣реИрдВ, рдЗрд╕реА рддрд░рд╣ рдХреА рд╡рд╕реНрддреБрдУрдВ рдХреЗ рдЗрд▓рд╛рдЬ рдХреЗ рд▓рд┐рдП рд░рд╛рдЬреНрдп рд╕рдВрд░рдХреНрд╖рдг рдФрд░ рдмрд╣реБрд░реВрдкрддрд╛ рдХреЗ рд▓рд┐рдП рдПрдирдХреИрдкреНрд╕реБрд▓реЗрд╢рдиред рдПрдХ рдбрд┐рдлрд╝реЙрд▓реНрдЯ рдХреЗ рд░реВрдк рдореЗрдВ рдЙрддреНрдкрд░рд┐рд╡рд░реНрддрди рднреА рд╕рдордЭ рдореЗрдВ рдЖрддрд╛ рд╣реИ, рдХреНрдпреЛрдВрдХрд┐ рдЗрд╕ рд╕рдВрджрд░реНрдн рдореЗрдВ рдЕрдкрд░рд┐рд╡рд░реНрддрдиреАрдп рд╡рд╕реНрддреБ рдХрд╛ рдЬреАрд╡рди рдЪрдХреНрд░ рдирд╣реАрдВ рд╣реЛ рд╕рдХрддрд╛ рд╣реИ рдФрд░ рд╣рдореЗрд╢рд╛ рдПрдХ рд░рд╛рдЬреНрдп рд╣реЛрддрд╛ рд╣реИ, рдЬреЛ рд╕рдмрд╕реЗ рдЖрдо рдорд╛рдорд▓рд╛ рдирд╣реАрдВ рд╣реИред


рдмрд╛рдд рдпрд╣ рд╣реИ рдХрд┐ рдЬрдм рдЖрдк рдЗрди рджрд┐рдиреЛрдВ рдХреЗ рдПрдХ рд╡рд┐рд╢рд┐рд╖реНрдЯ рд╡реЗрдм рдЕрдиреБрдкреНрд░рдпреЛрдЧ рдХреЛ рджреЗрдЦрддреЗ рд╣реИрдВ, рддреЛ рдпрд╣ рд╡рд╕реНрддреБрдУрдВ рдХреЗ рд╕рд╛рде рд╡реНрдпрд╡рд╣рд╛рд░ рдирд╣реАрдВ рдХрд░рддрд╛ рд╣реИред рд╣рдорд╛рд░реЗ рдХреЛрдб рдореЗрдВ рд▓рдЧрднрдЧ рд╣рд░ рдЪреАрдЬ рдореЗрдВ рдпрд╛ рддреЛ рд╢рд╛рд╢реНрд╡рдд рдЬреАрд╡рдирдХрд╛рд▓ рд╣реИ рдпрд╛ рдХреЛрдИ рдЙрдЪрд┐рдд рдЬреАрд╡рдирдХрд╛рд▓ рдирд╣реАрдВ рд╣реИред "рдСрдмреНрдЬреЗрдХреНрдЯреНрд╕" рдХреЗ рджреЛ рд╕рдмрд╕реЗ рдЖрдо рдкреНрд░рдХрд╛рд░ рдХреБрдЫ рдкреНрд░рдХрд╛рд░ рдХреА рд╕реЗрд╡рд╛рдПрдВ рд╣реИрдВ рдЬреИрд╕реЗ рдХрд┐ UserService , EmployeeRepository рдпрд╛ рдХреБрдЫ рдореЙрдбрд▓ / рд╕рдВрд╕реНрдерд╛рдПрдВ / рдбреАрдЯреАрдУ рдпрд╛ рдЬреЛ рднреА рдЖрдк рдЙрдиреНрд╣реЗрдВ рдХреЙрд▓ рдХрд░рддреЗ рд╣реИрдВред рд╕реЗрд╡рд╛рдУрдВ рдХреЗ рдЕрдВрджрд░ рдХреЛрдИ рддрд╛рд░реНрдХрд┐рдХ рд╕реНрдерд┐рддрд┐ рдирд╣реАрдВ рд╣реИ, рд╡реЗ рдорд░ рдЬрд╛рддреЗ рд╣реИрдВ рдФрд░ рдлрд┐рд░ рд╕реЗ рдЙрд╕реА рдХреЗ рд╕рдорд╛рди рдкреИрджрд╛ рд╣реЛрддреЗ рд╣реИрдВ, рд╣рдо рд╕рд┐рд░реНрдл рдПрдХ рдирдП рдбреЗрдЯрд╛рдмреЗрд╕ рдХрдиреЗрдХреНрд╢рди рдХреЗ рд╕рд╛рде рдирд┐рд░реНрднрд░рддрд╛ рдЧреНрд░рд╛рдл рдХреЛ рдлрд┐рд░ рд╕реЗ рдмрдирд╛рддреЗ рд╣реИрдВред


рд╕рдВрд╕реНрдерд╛рдУрдВ рдФрд░ рдореЙрдбрд▓реЛрдВ рдХреЗ рдкрд╛рд╕ рдХреЛрдИ рднреА рд╡реНрдпрд╡рд╣рд╛рд░ рдирд╣реАрдВ рд╣реИ, рд╡реЗ рдХреЗрд╡рд▓ рдбреЗрдЯрд╛ рдХреЗ рдмрдВрдбрд▓ рд╣реИрдВ, рдЙрдирдХреА рдЙрддреНрдкрд░рд┐рд╡рд░реНрддрди рдорджрдж рдирд╣реАрдВ рдХрд░рддрд╛ рд╣реИ рд▓реЗрдХрд┐рди рдХрд╛рдлреА рд╡рд┐рдкрд░реАрдд рд╣реИред


рдЗрд╕рд▓рд┐рдП OOP рдХреА рдкреНрд░рдореБрдЦ рд╡рд┐рд╢реЗрд╖рддрд╛рдПрдВ рдЗрд╕ рддрд░рд╣ рдХреЗ рдЕрдиреБрдкреНрд░рдпреЛрдЧреЛрдВ рдХреЛ рд╡рд┐рдХрд╕рд┐рдд рдХрд░рдиреЗ рдХреЗ рд▓рд┐рдП рд╡рд╛рд╕реНрддрд╡ рдореЗрдВ рдЙрдкрдпреЛрдЧреА рдирд╣реАрдВ рд╣реИрдВред


рдПрдХ рд╡рд┐рд╢рд┐рд╖реНрдЯ рд╡реЗрдм рдРрдк рдореЗрдВ рдХреНрдпрд╛ рд╣реЛрддрд╛ рд╣реИ рдбреЗрдЯрд╛ рдкреНрд░рд╡рд╛рд╣рд┐рдд рд╣реЛрддрд╛ рд╣реИ: рд╕рддреНрдпрд╛рдкрди, рдкрд░рд┐рд╡рд░реНрддрди, рдореВрд▓реНрдпрд╛рдВрдХрди рдФрд░ рдЗрд╕реА рддрд░рд╣ред рдФрд░ рдПрдХ рдкреНрд░рддрд┐рдорд╛рди рд╣реИ рдЬреЛ рдЙрд╕ рддрд░рд╣ рдХреА рдиреМрдХрд░реА рдХреЗ рд▓рд┐рдП рдкреВрд░реА рддрд░рд╣ рд╕реЗ рдлрд┐рдЯ рдмреИрдарддрд╛ рд╣реИ: рдХрд╛рд░реНрдпрд╛рддреНрдордХ рдкреНрд░реЛрдЧреНрд░рд╛рдорд┐рдВрдЧред рдФрд░ рдЗрд╕рдХреЗ рд▓рд┐рдП рдПрдХ рд╕рдмреВрдд рд╣реИ: рд▓реЛрдХрдкреНрд░рд┐рдп рднрд╛рд╖рд╛рдУрдВ рдореЗрдВ рд╕рднреА рдЖрдзреБрдирд┐рдХ рд╡рд┐рд╢реЗрд╖рддрд╛рдПрдВ рдЖрдЬ рд╡рд╣рд╛рдВ рд╕реЗ рдЖрддреА рд╣реИрдВ: async/await , рд▓реИрдореНрдмреНрдбрд╛ рдФрд░ рдкреНрд░рддрд┐рдирд┐рдзрд┐рдпреЛрдВ, рдкреНрд░рддрд┐рдХреНрд░рд┐рдпрд╛рд╢реАрд▓ рдкреНрд░реЛрдЧреНрд░рд╛рдорд┐рдВрдЧ, рднреЗрджрднрд╛рд╡ рд░рд╣рд┐рдд рдпреВрдирд┐рдпрдиреЛрдВ (рд╕реНрд╡рд┐рдлреНрдЯ рдпрд╛ рдЬрдВрдЧ рдореЗрдВ enums, java рдпрд╛ .net рдореЗрдВ enums рд╕реЗ рднреНрд░рдорд┐рдд рд╣реЛрдиреЗ рдХреЗ рд▓рд┐рдП рдирд╣реАрдВ)ред ), рдЯреНрдпреВрдкрд▓реНрд╕ - рд╡рд╣ рд╕рдм рдЬреЛ рдПрдлрдкреА рд╕реЗ рд╣реИред


рд╣рд╛рд▓рд╛рдБрдХрд┐, рд╡реЗ рд╕рд┐рд░реНрдл crumbles рд╣реИрдВ, рдпрд╣ рдЙрдирдХреЗ рд▓рд┐рдП рдмрд╣реБрдд рдЕрдЪреНрдЫрд╛ рд╣реИ, рд▓реЗрдХрд┐рди рд╡рд╣рд╛рдБ рдЕрдзрд┐рдХ рд╣реИ, рдЬрд┐рд╕ рддрд░рд╣ рд╕реЗ рдЕрдзрд┐рдХ рд╣реИред


рдЗрд╕рд╕реЗ рдкрд╣рд▓реЗ рдХрд┐ рдореИрдВ рдХрд┐рд╕реА рднреА рдЧрд╣рд░рд╛рдИ рддрдХ рдЬрд╛рдКрдБ, рд╡рд╣рд╛рдБ рдПрдХ рдмрд┐рдВрджреБ рдмрдирд╛рдпрд╛ рдЬрд╛рдирд╛ рдЪрд╛рд╣рд┐рдПред рдПрдХ рдирдИ рднрд╛рд╖рд╛, рд╡рд┐рд╢реЗрд╖ рд░реВрдк рд╕реЗ рдПрдХ рдирдП рдкреНрд░рддрд┐рдорд╛рди рдкрд░ рд╕реНрд╡рд┐рдЪ рдХрд░рдирд╛, рдбреЗрд╡рд▓рдкрд░реНрд╕ рдХреЗ рд▓рд┐рдП рдПрдХ рдирд┐рд╡реЗрд╢ рд╣реИ рдФрд░ рдЗрд╕рд▓рд┐рдП рд╡реНрдпрд╛рдкрд╛рд░ рдХреЗ рд▓рд┐рдП рд╣реИред рдореВрд░реНрдЦрддрд╛рдкреВрд░реНрдг рдирд┐рд╡реЗрд╢ рдХрд░рдиреЗ рд╕реЗ рдЖрдкрдХреЛ рдкрд░реЗрд╢рд╛рдиреА рдХреЗ рдЕрд▓рд╛рд╡рд╛ рдХреБрдЫ рдирд╣реАрдВ рдорд┐рд▓реЗрдЧрд╛, рд▓реЗрдХрд┐рди рдЙрдЪрд┐рдд рдирд┐рд╡реЗрд╢ рдмрд╣реБрдд рд╣реА рдХрд╛рдо рдХреА рдЪреАрдЬ рд╣реЛ рд╕рдХрддреА рд╣реИ рдЬреЛ рдЖрдкрдХреЛ рдмрдЪрд╛рдП рд░рдЦреЗрдЧреАред


рдЙрдкрдХрд░рдг рд╣рдорд╛рд░реЗ рдкрд╛рд╕ рд╣реИрдВ рдФрд░ рд╡реЗ рд╣рдореЗрдВ рдХреНрдпрд╛ рджреЗрддреЗ рд╣реИрдВ


рд╣рдо рдореЗрдВ рд╕реЗ рдмрд╣реБрдд рд╕реЗ рд▓реЛрдЧ рд╕реНрдереИрддрд┐рдХ рдЯрд╛рдЗрдкрд┐рдВрдЧ рд╡рд╛рд▓реА рднрд╛рд╖рд╛рдПрдБ рдкрд╕рдВрдж рдХрд░рддреЗ рд╣реИрдВред рдЗрд╕рдХрд╛ рдХрд╛рд░рдг рд╕рд░рд▓ рд╣реИ: рдХрдореНрдкрд╛рдЗрд▓рд░ рдХрд╛рд░реНрдпреЛрдВ рдХреЗ рд▓рд┐рдП рдЙрдЪрд┐рдд рдорд╛рдкрджрдВрдбреЛрдВ рдХреЛ рдкрд╛рд░рд┐рдд рдХрд░рдиреЗ, рд╣рдорд╛рд░реА рд╕рдВрд╕реНрдерд╛рдУрдВ рдХреЛ рд╕рд╣реА рдврдВрдЧ рд╕реЗ рдФрд░ рдЗрд╕реА рддрд░рд╣ рдмрдирд╛рдиреЗ рдЬреИрд╕реЗ рдердХрд╛рдК рдЪреЗрдХреЛрдВ рдХрд╛ рдзреНрдпрд╛рди рд░рдЦрддрд╛ рд╣реИред рдпреЗ рдЪреЗрдХ рдореБрдлреНрдд рдореЗрдВ рдЖрддреЗ рд╣реИрдВред рдЕрдм, рд╕рдВрдХрд▓рдХ рдХреА рдЬрд╛рдБрдЪ рдирд╣реАрдВ рдХрд░ рд╕рдХрдиреЗ рд╡рд╛рд▓реЗ рд╕рд╛рдорд╛рди рдХреЗ рд▓рд┐рдП, рд╣рдорд╛рд░реЗ рдкрд╛рд╕ рдПрдХ рд╡рд┐рдХрд▓реНрдк рд╣реИ: рд╕рд░реНрд╡реЛрддреНрддрдо рдХреЗ рд▓рд┐рдП рдЖрд╢рд╛ рдХрд░реЗрдВ рдпрд╛ рдХреБрдЫ рдкрд░реАрдХреНрд╖рдг рдХрд░реЗрдВред рдкрд░реАрдХреНрд╖рдг рд▓рд┐рдЦрдиреЗ рдХрд╛ рдЕрд░реНрде рд╣реИ рдкреИрд╕рд╛, рдФрд░ рдЖрдк рдкрд░реАрдХреНрд╖рдг рдХреЗ рдЕрдиреБрд╕рд╛рд░ рд╕рд┐рд░реНрдл рдПрдХ рдмрд╛рд░ рднреБрдЧрддрд╛рди рдирд╣реАрдВ рдХрд░рддреЗ рд╣реИрдВ, рдЖрдкрдХреЛ рдЙрдиреНрд╣реЗрдВ рдмрдирд╛рдП рд░рдЦрдирд╛ рд╣реЛрдЧрд╛ред рдЗрд╕рдХреЗ рдЕрд▓рд╛рд╡рд╛, рд▓реЛрдЧ рд╕реБрд╕реНрдд рд╣реЛ рдЬрд╛рддреЗ рд╣реИрдВ, рдЗрд╕рд▓рд┐рдП рд╣рд░ рдмрд╛рд░ рдПрдХ рд╕рдордп рдореЗрдВ рд╣рдореЗрдВ рдЭреВрдареЗ рд╕рдХрд╛рд░рд╛рддреНрдордХ рдФрд░ рдЭреВрдареЗ рдирдХрд╛рд░рд╛рддреНрдордХ рдкрд░рд┐рдгрд╛рдо рдорд┐рд▓рддреЗ рд╣реИрдВред рдЖрдкрдХреЛ рдЬрд┐рддрдиреЗ рдЕрдзрд┐рдХ рдкрд░реАрдХреНрд╖рдг рд▓рд┐рдЦрдиреЗ рд╣реИрдВ, рдЙрди рдкрд░реАрдХреНрд╖рдгреЛрдВ рдХреА рдФрд╕рдд рдЧреБрдгрд╡рддреНрддрд╛ рдЙрддрдиреА рд╣реА рдХрдо рд╣реИред рдПрдХ рдФрд░ рд╕рдорд╕реНрдпрд╛ рд╣реИ: рдХрд┐рд╕реА рдЪреАрдЬрд╝ рдХрд╛ рдкрд░реАрдХреНрд╖рдг рдХрд░рдиреЗ рдХреЗ рд▓рд┐рдП, рдЖрдкрдХреЛ рдпрд╣ рдЬрд╛рдирдирд╛ рдФрд░ рдпрд╛рдж рд░рдЦрдирд╛ рд╣реЛрдЧрд╛ рдХрд┐ рдЙрд╕ рдЪреАрдЬрд╝ рдХрд╛ рдкрд░реАрдХреНрд╖рдг рдХрд┐рдпрд╛ рдЬрд╛рдирд╛ рдЪрд╛рд╣рд┐рдП, рд▓реЗрдХрд┐рди рдЬрд┐рддрдирд╛ рдмрдбрд╝рд╛ рдЖрдкрдХрд╛ рд╕рд┐рд╕реНрдЯрдо рдЙрддрдирд╛ рд╣реА рдЖрд╕рд╛рди рд╣реЛрдЧрд╛ рдХрд┐ рд╡рд╣ рдХрд┐рд╕реА рдЪреАрдЬрд╝ рдХреЛ рдпрд╛рдж рдХрд░ рд╕рдХреЗред


рд╣рд╛рд▓рд╛рдБрдХрд┐ рд╕рдВрдХрд▓рдХ рдХреЗрд╡рд▓ рднрд╛рд╖рд╛ рдХреЗ рдкреНрд░рдХрд╛рд░ рдХреЗ рд░реВрдк рдореЗрдВ рдЕрдЪреНрдЫрд╛ рд╣реИред рдпрджрд┐ рдпрд╣ рдЖрдкрдХреЛ рд╕реНрдереИрддрд┐рдХ рддрд░реАрдХреЛрдВ рд╕реЗ рдХреБрдЫ рд╡реНрдпрдХреНрдд рдХрд░рдиреЗ рдХреА рдЕрдиреБрдорддрд┐ рдирд╣реАрдВ рджреЗрддрд╛ рд╣реИ, рддреЛ рдЖрдкрдХреЛ рд░рдирдЯрд╛рдЗрдо рдореЗрдВ рдРрд╕рд╛ рдХрд░рдирд╛ рд╣реЛрдЧрд╛ред рдЬрд┐рд╕рдХрд╛ рдЕрд░реНрде рд╣реИ рдкрд░реАрдХреНрд╖рдг, рд╣рд╛рдБред рдпрд╣ рдХреЗрд╡рд▓ рдкреНрд░рдХрд╛рд░ рдкреНрд░рдгрд╛рд▓реА рдХреЗ рдмрд╛рд░реЗ рдореЗрдВ рдирд╣реАрдВ рд╣реИ, рд╣рд╛рд▓рд╛рдВрдХрд┐, рд╕рд┐рдВрдЯреИрдХреНрд╕ рдФрд░ рдЫреЛрдЯреА рдЪреАрдиреА рд╡рд┐рд╢реЗрд╖рддрд╛рдПрдВ рдмрд╣реБрдд рдорд╣рддреНрд╡рдкреВрд░реНрдг рд╣реИрдВ, рдХреНрдпреЛрдВрдХрд┐ рджрд┐рди рдХреЗ рдЕрдВрдд рдореЗрдВ рд╣рдо рдЬрд┐рддрдирд╛ рд╕рдВрднрд╡ рд╣реЛ рдЙрддрдирд╛ рдХрдо рдХреЛрдб рд▓рд┐рдЦрдирд╛ рдЪрд╛рд╣рддреЗ рд╣реИрдВ, рдЗрд╕рд▓рд┐рдП рдпрджрд┐ рдХреБрдЫ рджреГрд╖реНрдЯрд┐рдХреЛрдг рд╕реЗ рдЖрдкрдХреЛ рджрд╕ рдЧреБрдирд╛ рдЕрдзрд┐рдХ рд▓рд╛рдЗрдиреЗрдВ рд▓рд┐рдЦрдиреЗ рдХреА рдЖрд╡рд╢реНрдпрдХрддрд╛ рд╣реЛрддреА рд╣реИ, рддреЛ рдареАрдХ рд╣реИ, рдХреЛрдИ рднреА рдЗрд╕рдХрд╛ рдЙрдкрдпреЛрдЧ рдХрд░рдиреЗ рд╡рд╛рд▓рд╛ рдирд╣реАрдВ рд╣реИред рдЗрд╕рд▓рд┐рдП рдпрд╣ рдорд╣рддреНрд╡рдкреВрд░реНрдг рд╣реИ рдХрд┐ рдЖрдкрдХреЗ рджреНрд╡рд╛рд░рд╛ рдЪреБрдиреА рдЧрдИ рднрд╛рд╖рд╛ рдореЗрдВ рд╕реБрд╡рд┐рдзрд╛рдУрдВ рдФрд░ рдЯреНрд░рд┐рдХреНрд╕ рдХрд╛ рдлрд┐рдЯрд┐рдВрдЧ рд╕реЗрдЯ рд╣реЛ - рдЕрдЪреНрдЫреА рддрд░рд╣ рд╕реЗ, рд╕рдордЧреНрд░ рд░реВрдк рд╕реЗ рд╕рд╣реА рдлреЛрдХрд╕ред рдпрджрд┐ рдпрд╣ рдирд╣реАрдВ рд╣реИ - рдЕрдкрдиреЗ рд╕рд┐рд╕реНрдЯрдо рдХреА рдЬрдЯрд┐рд▓рддрд╛ рдФрд░ рдмрджрд▓рддреА рдЖрд╡рд╢реНрдпрдХрддрд╛рдУрдВ рдЬреИрд╕реА рдореВрд▓ рдЪреБрдиреМрддрд┐рдпреЛрдВ рд╕реЗ рд▓рдбрд╝рдиреЗ рдХреЗ рд▓рд┐рдП рдЗрд╕рдХреА рд╕реБрд╡рд┐рдзрд╛рдУрдВ рдХрд╛ рдЙрдкрдпреЛрдЧ рдХрд░рдиреЗ рдХреЗ рдмрдЬрд╛рдп, рдЖрдк рднрд╛рд╖рд╛ рд╕реЗ рднреА рд▓рдбрд╝рдиреЗ рд╡рд╛рд▓реЗ рд╣реИрдВред рдФрд░ рдпрд╣ рд╕рдм рдкреИрд╕реЗ рдХреЗ рд▓рд┐рдП рдиреАрдЪреЗ рдЖрддрд╛ рд╣реИ, рдХреНрдпреЛрдВрдХрд┐ рдЖрдк рдбреЗрд╡рд▓рдкрд░реНрд╕ рдХреЛ рдЙрдирдХреЗ рд╕рдордп рдХреЗ рд▓рд┐рдП рднреБрдЧрддрд╛рди рдХрд░рддреЗ рд╣реИрдВред рдЙрдиреНрд╣реЗрдВ рдЬрд┐рддрдиреА рдЕрдзрд┐рдХ рд╕рдорд╕реНрдпрд╛ рдХрд╛ рд╕рдорд╛рдзрд╛рди рдХрд░рдирд╛ рд╣реЛрдЧрд╛, рдЙрддрдиреЗ рд╕рдордп рдХреЗ рд▓рд┐рдП рдЙрдиреНрд╣реЗрдВ рдЬрд░реВрд░рдд рд╣реЛрдЧреА рдФрд░ рдЙрддрдиреА рд╣реА рдЕрдзрд┐рдХ рдбреЗрд╡рд▓рдкрд░реНрд╕ рдХреА рдЬрд░реВрд░рдд рд╣реЛрдЧреАред


рдЕрдВрдд рдореЗрдВ рд╣рдо рд╕рднреА рдХреЛ рд╕рд╛рдмрд┐рдд рдХрд░рдиреЗ рдХреЗ рд▓рд┐рдП рдХреБрдЫ рдХреЛрдб рджреЗрдЦрдиреЗ рд╡рд╛рд▓реЗ рд╣реИрдВред рдореИрдВ рдПрдХ .NET рдбреЗрд╡рд▓рдкрд░ рд╣реЛрдиреЗ рдЬрд╛ рд░рд╣рд╛ рд╣реВрдВ, рдЗрд╕рд▓рд┐рдП рдХреЛрдб рдирдореВрдиреЗ C # рдФрд░ F # рдореЗрдВ рдЖрдиреЗ рд╡рд╛рд▓реЗ рд╣реИрдВ, рд▓реЗрдХрд┐рди рд╕рд╛рдорд╛рдиреНрдп рдЪрд┐рддреНрд░ рдХрдореЛрдмреЗрд╢ рдЕрдиреНрдп рд▓реЛрдХрдкреНрд░рд┐рдп OOP рдФрд░ FP рднрд╛рд╖рд╛рдУрдВ рдореЗрдВ рд╕рдорд╛рди рд╣реЛрдВрдЧреЗред


рдХреЛрдбрд┐рдВрдЧ рд╢реБрд░реВ рдХрд░рддреЗ рд╣реИрдВ


рд╣рдо рдХреНрд░реЗрдбрд┐рдЯ рдХрд╛рд░реНрдб рдХреЗ рдкреНрд░рдмрдВрдзрди рдХреЗ рд▓рд┐рдП рдПрдХ рд╡реЗрдм рдПрдкреНрд▓рд┐рдХреЗрд╢рди рдмрдирд╛рдиреЗ рдЬрд╛ рд░рд╣реЗ рд╣реИрдВред


рдмреБрдирд┐рдпрд╛рджреА рдЖрд╡рд╢реНрдпрдХрддрд╛рдПрдВ:


  • рдЙрдкрдпреЛрдЧрдХрд░реНрддрд╛ рдмрдирд╛рдПрдВ / рдкрдврд╝реЗрдВ
  • рдХреНрд░реЗрдбрд┐рдЯ рдХрд╛рд░реНрдб рдмрдирд╛рдПрдВ / рдкрдврд╝реЗрдВ
  • рдХреНрд░реЗрдбрд┐рдЯ рдХрд╛рд░реНрдб рд╕рдХреНрд░рд┐рдп / рдирд┐рд╖реНрдХреНрд░рд┐рдп рдХрд░реЗрдВ
  • рдХрд╛рд░реНрдб рдХреЗ рд▓рд┐рдП рджреИрдирд┐рдХ рд╕реАрдорд╛ рдирд┐рд░реНрдзрд╛рд░рд┐рдд рдХрд░реЗрдВ
  • рдЯреЙрдк рдЕрдк рдмреИрд▓реЗрдВрд╕
  • рдкреНрд░рдХреНрд░рд┐рдпрд╛ рднреБрдЧрддрд╛рди (рд╢реЗрд╖ рд░рд╛рд╢рд┐, рдХрд╛рд░реНрдб рд╕рдорд╛рдкреНрддрд┐ рдХреА рддрд╛рд░реАрдЦ, рд╕рдХреНрд░рд┐рдп / рдирд┐рд╖реНрдХреНрд░рд┐рдп рд╕реНрдерд┐рддрд┐ рдФрд░ рджреИрдирд┐рдХ рд╕реАрдорд╛ рдкрд░ рд╡рд┐рдЪрд╛рд░)

рд╕рд╛рджрдЧреА рдХреЗ рд▓рд┐рдП рд╣рдо рдкреНрд░рддрд┐ рдХрд╛рд░реНрдб рдПрдХ рдХрд╛рд░реНрдб рдХрд╛ рдЙрдкрдпреЛрдЧ рдХрд░рдиреЗ рд╡рд╛рд▓реЗ рд╣реИрдВ рдФрд░ рд╣рдо рдкреНрд░рд╛рдзрд┐рдХрд░рдг рдХреЛ рдЫреЛрдбрд╝ рджреЗрдВрдЧреЗред рд▓реЗрдХрд┐рди рдмрд╛рдХреА рдХреЗ рд▓рд┐рдП рд╣рдо рд╕рддреНрдпрд╛рдкрди, рддреНрд░реБрдЯрд┐ рд╕реЗ рдирд┐рдкрдЯрдиреЗ, рдбреЗрдЯрд╛рдмреЗрд╕ рдФрд░ рд╡реЗрдм рдПрдкреАрдЖрдИ рдХреЗ рд╕рд╛рде рд╕рдХреНрд╖рдо рдПрдкреНрд▓рд┐рдХреЗрд╢рди рдмрдирд╛рдиреЗ рдЬрд╛ рд░рд╣реЗ рд╣реИрдВред рддреЛ рдЪрд▓рд┐рдП рдЕрдкрдиреЗ рдкрд╣рд▓реЗ рдХрд╛рд░реНрдп рдкрд░ рдЖрддреЗ рд╣реИрдВ: рдХреНрд░реЗрдбрд┐рдЯ рдХрд╛рд░реНрдб рдбрд┐рдЬрд╝рд╛рдЗрди рдХрд░реЗрдВред


рдкрд╣рд▓реЗ, рдЖрдЗрдП рджреЗрдЦреЗрдВ рдХрд┐ рдпрд╣ C # рдореЗрдВ рдХреИрд╕рд╛ рджрд┐рдЦреЗрдЧрд╛


 public class Card { public string CardNumber {get;set;} public string Name {get;set;} public int ExpirationMonth {get;set;} public int ExpirationYear {get;set;} public bool IsActive {get;set;} public AccountInfo AccountInfo {get;set;} } public class AccountInfo { public decimal Balance {get;set;} public string CardNumber {get;set;} public decimal DailyLimit {get;set;} } 

рд▓реЗрдХрд┐рди рдпрд╣ рдкрд░реНрдпрд╛рдкреНрдд рдирд╣реАрдВ рд╣реИ, рд╣рдореЗрдВ рд╕рддреНрдпрд╛рдкрди рдХреЛ рдЬреЛрдбрд╝рдирд╛ рд╣реЛрдЧрд╛, рдФрд░ рдЖрдорддреМрд░ рдкрд░ рдпрд╣ рдХреБрдЫ Validator рдореЗрдВ рдХрд┐рдпрд╛ рдЬрд╛ рд░рд╣рд╛ рд╣реИ, рдЬреИрд╕реЗ рдХрд┐ FluentValidation ред


рдирд┐рдпрдо рд╕рд░рд▓ рд╣реИрдВ:


  • рдХрд╛рд░реНрдб рдирдВрдмрд░ рдЖрд╡рд╢реНрдпрдХ рд╣реИ рдФрд░ 16 рдЕрдВрдХреЛрдВ рдХрд╛ рд╕реНрдЯреНрд░рд┐рдВрдЧ рд╣реЛрдирд╛ рдЪрд╛рд╣рд┐рдПред
  • рдирд╛рдо рдХреА рдЖрд╡рд╢реНрдпрдХрддрд╛ рд╣реИ рдФрд░ рдЗрд╕рдореЗрдВ рдХреЗрд╡рд▓ рдЕрдХреНрд╖рд░ рд╣реЛрдиреЗ рдЪрд╛рд╣рд┐рдП рдФрд░ рдмреАрдЪ рдореЗрдВ рд░рд┐рдХреНрдд рд╕реНрдерд╛рди рд╣реЛ рд╕рдХрддреЗ рд╣реИрдВред
  • рдорд╣реАрдирд╛ рдФрд░ рд╕рд╛рд▓ рд╕реАрдорд╛рдУрдВ рдХреЛ рд╕рдВрддреБрд╖реНрдЯ рдХрд░рдирд╛ рд╣реИред
  • рдХрд╛рд░реНрдб рдХреЗ рд╕рдХреНрд░рд┐рдп рд╣реЛрдиреЗ рдФрд░ рдХрд╛рд░реНрдб рдХреЗ рдирд┐рд╖реНрдХреНрд░рд┐рдп рд╣реЛрдиреЗ рдкрд░ рдЦрд╛рддрд╛ рдЬрд╛рдирдХрд╛рд░реА рдореМрдЬреВрдж рд╣реЛрдиреА рдЪрд╛рд╣рд┐рдПред рдпрджрд┐ рдЖрдк рд╕реЛрдЪ рд░рд╣реЗ рд╣реИрдВ рдХрд┐ рдХреНрдпреЛрдВ, рдпрд╣ рд╕рд░рд▓ рд╣реИ: рдЬрдм рдХрд╛рд░реНрдб рдХреЛ рдирд┐рд╖реНрдХреНрд░рд┐рдп рдХрд░ рджрд┐рдпрд╛ рдЬрд╛рддрд╛ рд╣реИ, рддреЛ рд╢реЗрд╖ рд░рд╛рд╢рд┐ рдпрд╛ рджреИрдирд┐рдХ рд╕реАрдорд╛ рдХреЛ рдмрджрд▓рдирд╛ рд╕рдВрднрд╡ рдирд╣реАрдВ рд╣реЛрдирд╛ рдЪрд╛рд╣рд┐рдПред

 public class CardValidator : IValidator { internal static CardNumberRegex = new Regex("^[0-9]{16}$"); internal static NameRegex = new Regex("^[\w]+[\w ]+[\w]+$"); public CardValidator() { RuleFor(x => x.CardNumber) .Must(c => !string.IsNullOrEmpty(c) && CardNumberRegex.IsMatch(c)) .WithMessage("oh my"); RuleFor(x => x.Name) .Must(c => !string.IsNullOrEmpty(c) && NameRegex.IsMatch(c)) .WithMessage("oh no"); RuleFor(x => x.ExpirationMonth) .Must(x => x >= 1 && x <= 12) .WithMessage("oh boy"); RuleFor(x => x.ExpirationYear) .Must(x => x >= 2019 && x <= 2023) .WithMessage("oh boy"); RuleFor(x => x.AccountInfo) .Null() .When(x => !x.IsActive) .WithMessage("oh boy"); RuleFor(x => x.AccountInfo) .NotNull() .When(x => x.IsActive) .WithMessage("oh boy"); } } 

рдЕрдм рдЗрд╕ рджреГрд╖реНрдЯрд┐рдХреЛрдг рдХреЗ рд╕рд╛рде рдХрдИ рд╕рдорд╕реНрдпрд╛рдПрдВ рд╣реИрдВ:


  • рд╕рддреНрдпрд╛рдкрди рдХреЛ рдкреНрд░рдХрд╛рд░ рдХреА рдШреЛрд╖рдгрд╛ рд╕реЗ рдЕрд▓рдЧ рдХрд┐рдпрд╛ рдЧрдпрд╛ рд╣реИ, рдЬрд┐рд╕рдХрд╛ рдЕрд░реНрде рд╣реИ рдХрд┐ рд╣рдореЗрдВ рд╡рд╛рд╕реНрддрд╡ рдореЗрдВ рдХрд┐рд╕ рдХрд╛рд░реНрдб рдХреА рдкреВрд░реА рддрд╕реНрд╡реАрд░ рджреЗрдЦрдиреА рд╣реИ, рдХреЛрдб рдХреЗ рдорд╛рдзреНрдпрдо рд╕реЗ рдиреЗрд╡рд┐рдЧреЗрдЯ рдХрд░рдирд╛ рд╣реЛрдЧрд╛ рдФрд░ рд╣рдорд╛рд░реЗ рд╕рд┐рд░ рдореЗрдВ рдЗрд╕ рдЫрд╡рд┐ рдХреЛ рдлрд┐рд░ рд╕реЗ рдмрдирд╛рдирд╛ рд╣реЛрдЧрд╛ред рдпрд╣ рдПрдХ рдмрдбрд╝реА рд╕рдорд╕реНрдпрд╛ рдирд╣реАрдВ рд╣реИ рдЬрдм рдпрд╣ рдХреЗрд╡рд▓ рдПрдХ рдмрд╛рд░ рд╣реЛрддрд╛ рд╣реИ, рд▓реЗрдХрд┐рди рдЬрдм рд╣рдореЗрдВ рдПрдХ рдмрдбрд╝реА рдкрд░рд┐рдпреЛрдЬрдирд╛ рдореЗрдВ рд╣рд░ рдПрдХ рдЗрдХрд╛рдИ рдХреЗ рд▓рд┐рдП рдРрд╕рд╛ рдХрд░рдирд╛ рд╣реИ, рддреЛ рдареАрдХ рд╣реИ, рдпрд╣ рдмрд╣реБрдд рд╕рдордп рд▓реЗрдиреЗ рд╡рд╛рд▓рд╛ рд╣реИред
  • рдпрд╣ рд╕рддреНрдпрд╛рдкрди рдордЬрдмреВрд░ рдирд╣реАрдВ рд╣реИ, рд╣рдореЗрдВ рдЗрд╕реЗ рд╣рд░ рдЬрдЧрд╣ рдЙрдкрдпреЛрдЧ рдХрд░рдиреЗ рдХреЗ рд▓рд┐рдП рдзреНрдпрд╛рди рдореЗрдВ рд░рдЦрдирд╛ рд╣реЛрдЧрд╛ред рд╣рдо рдкрд░реАрдХреНрд╖рдгреЛрдВ рдХреЗ рд╕рд╛рде рдЗрд╕реЗ рд╕реБрдирд┐рд╢реНрдЪрд┐рдд рдХрд░ рд╕рдХрддреЗ рд╣реИрдВ, рд▓реЗрдХрд┐рди рдлрд┐рд░, рдЖрдкрдХреЛ рдкрд░реАрдХреНрд╖рдг рд▓рд┐рдЦрддреЗ рд╕рдордп рдЗрд╕рдХреЗ рдмрд╛рд░реЗ рдореЗрдВ рдпрд╛рдж рд░рдЦрдирд╛ рд╣реЛрдЧрд╛ред
  • рдЬрдм рд╣рдо рдХрд╛рд░реНрдб рдирдВрдмрд░ рдХреЛ рдЕрдиреНрдп рд╕реНрдерд╛рдиреЛрдВ рдкрд░ рдорд╛рдиреНрдп рдХрд░рдирд╛ рдЪрд╛рд╣рддреЗ рд╣реИрдВ, рддреЛ рд╣рдореЗрдВ рдлрд┐рд░ рд╕реЗ рд╡рд╣реА рдХрд╛рдо рдХрд░рдирд╛ рд╣реЛрдЧрд╛ред рдЬрд╝рд░реВрд░, рд╣рдо regex рдХреЛ рдПрдХ рд╕рд╛рдорд╛рдиреНрдп рд╕реНрдерд╛рди рдкрд░ рд░рдЦ рд╕рдХрддреЗ рд╣реИрдВ, рд▓реЗрдХрд┐рди рдлрд┐рд░ рднреА рд╣рдореЗрдВ рдЗрд╕реЗ рд╣рд░ рд╕рддреНрдпрд╛рдкрдирдХрд░реНрддрд╛ рдореЗрдВ рдХреЙрд▓ рдХрд░рдирд╛ рд╣реЛрдЧрд╛ред

F # рдореЗрдВ рд╣рдо рдЗрд╕реЗ рдЕрд▓рдЧ рддрд░реАрдХреЗ рд╕реЗ рдХрд░ рд╕рдХрддреЗ рд╣реИрдВ:


 (*{- First we define a type for CardNumber with private constructor and public factory which receives string and returns `Result<CardNumber, string>`. Normally we would use `ValidationError` instead, but string is good enough for example -}*) type CardNumber = private CardNumber of string with member this.Value = match this with CardNumber s -> s static member create str = match str with | (null|"") -> Error "card number can't be empty" | str -> if cardNumberRegex.IsMatch(str) then CardNumber str |> Ok else Error "Card number must be a 16 digits string" (*{- Then in here we express this logic "when card is deactivated, balance and daily limit manipulations aren't available". Note that this is way easier to grasp that reading `RuleFor()` in validators. -}*) type CardAccountInfo = | Active of AccountInfo | Deactivated (*{- And then that's it. The whole set of rules is here, and it's described in a static way. We don't need tests for that, the compiler is our test. And we can't accidentally miss this validation. -}*) type Card = { CardNumber: CardNumber Name: LetterString //-- LetterString is another type with built-in validation HolderId: UserId Expiration: (Month * Year) AccountDetails: CardAccountInfo } 

рдмреЗрд╢рдХ рдпрд╣рд╛рдБ рд╕реЗ рдХреБрдЫ рдЪреАрдЬреЗрдВ рд╣рдо C # рдореЗрдВ рдХрд░ рд╕рдХрддреЗ рд╣реИрдВред рд╣рдо CardNumber рдХреНрд▓рд╛рд╕ рдмрдирд╛ рд╕рдХрддреЗ рд╣реИрдВ рдЬреЛ CardNumber рдХреЛ рд╡рд╣рд╛рдВ рднреА рдлреЗрдВрдХ рджреЗрдЧрд╛ред рд▓реЗрдХрд┐рди CardAccountInfo рд╕рд╛рде рдпрд╣ рдЯреНрд░рд┐рдХ рдЖрд╕рд╛рди рддрд░реАрдХреЗ рд╕реЗ C # рдореЗрдВ рдирд╣реАрдВ рдХрд┐рдпрд╛ рдЬрд╛ рд╕рдХрддрд╛ рд╣реИред
рдПрдХ рдФрд░ рдмрд╛рдд - рд╕реА # рдЕрдкрд╡рд╛рджреЛрдВ рдкрд░ рднрд╛рд░реА рдирд┐рд░реНрднрд░ рдХрд░рддреА рд╣реИред рдЗрд╕рдХреЗ рд╕рд╛рде рдХрдИ рд╕рдорд╕реНрдпрд╛рдПрдВ рд╣реИрдВ:


  • рдЕрдкрд╡рд╛рдж рдореЗрдВ "рдЕрд░реНрде" рдЬрд╛рдирд╛ рдЬрд╛рддрд╛ рд╣реИред рдПрдХ рдХреНрд╖рдг рддреБрдо рдЗрд╕ рд╡рд┐рдзрд┐ рдореЗрдВ рдпрд╣рд╛рдВ рд╣реЛ, рджреВрд╕рд░рд╛ - рддреБрдо рдХрд┐рд╕реА рд╡реИрд╢реНрд╡рд┐рдХ рд╣реИрдВрдбрд▓рд░ рдореЗрдВ рд╕рдорд╛рдкреНрдд рд╣реЛ рдЧрдПред
  • рд╡реЗ рд╡рд┐рдзрд┐ рд╣рд╕реНрддрд╛рдХреНрд╖рд░ рдореЗрдВ рджрд┐рдЦрд╛рдИ рдирд╣реАрдВ рджреЗрддреЗ рд╣реИрдВред ValidationException рдпрд╛ InvalidUserOperationException рдЬреИрд╕реЗ рдЕрдкрд╡рд╛рдж рдЕрдиреБрдмрдВрдз рдХрд╛ рд╣рд┐рд╕реНрд╕рд╛ рд╣реИрдВ, рд▓реЗрдХрд┐рди рдЖрдк рдпрд╣ рдирд╣реАрдВ рдЬрд╛рдирддреЗ рдХрд┐ рдЬрдм рддрдХ рдЖрдк рдЗрд╕реЗ рд▓рд╛рдЧреВ рдирд╣реАрдВ рдХрд░рддреЗ рд╣реИрдВред рдФрд░ рдпрд╣ рдПрдХ рдмрдбрд╝реА рд╕рдорд╕реНрдпрд╛ рд╣реИ, рдХреНрдпреЛрдВрдХрд┐ рдХрд╛рдлреА рдмрд╛рд░ рдЖрдкрдХреЛ рдХрд┐рд╕реА рдФрд░ рджреНрд╡рд╛рд░рд╛ рд▓рд┐рдЦреЗ рдЧрдП рдХреЛрдб рдХрд╛ рдЙрдкрдпреЛрдЧ рдХрд░рдирд╛ рдкрдбрд╝рддрд╛ рд╣реИ, рдФрд░ рдХреЗрд╡рд▓ рд╣рд╕реНрддрд╛рдХреНрд╖рд░ рдХреЛ рдкрдврд╝рдиреЗ рдХреЗ рдмрдЬрд╛рдп, рдЖрдкрдХреЛ рдХреЙрд▓ рд╕реНрдЯреИрдХ рдХреЗ рдирд┐рдЪрд▓реЗ рднрд╛рдЧ рдореЗрдВ рд╕рднреА рддрд░рд╣ рд╕реЗ рдиреЗрд╡рд┐рдЧреЗрдЯ рдХрд░рдирд╛ рд╣реЛрдЧрд╛, рдЬрд┐рд╕рдореЗрдВ рдмрд╣реБрдд рд╕рдордп рд▓рдЧрддрд╛ рд╣реИред

рдФрд░ рдпрд╣ рдореБрдЭреЗ рдкрд░реЗрд╢рд╛рди рдХрд░рддрд╛ рд╣реИ: рдЬрдм рднреА рдореИрдВ рдХрд┐рд╕реА рдирдИ рд╕реБрд╡рд┐рдзрд╛ рдХреЛ рд▓рд╛рдЧреВ рдХрд░рддрд╛ рд╣реВрдВ, рддреЛ рдХрд╛рд░реНрдпрд╛рдиреНрд╡рдпрди рдкреНрд░рдХреНрд░рд┐рдпрд╛ рдореЗрдВ рдЕрдзрд┐рдХ рд╕рдордп рдирд╣реАрдВ рд▓рдЧрддрд╛ рд╣реИ, рдЗрд╕рдХрд╛ рдЕрдзрд┐рдХрд╛рдВрд╢ рднрд╛рдЧ рджреЛ рдЪрд░рдгреЛрдВ рдореЗрдВ рдЬрд╛рддрд╛ рд╣реИ:


  • рдЕрдиреНрдп рд▓реЛрдЧреЛрдВ рдХреЗ рдХреЛрдб рдХреЛ рдкрдврд╝рдирд╛ рдФрд░ рд╡реНрдпрд╛рд╡рд╕рд╛рдпрд┐рдХ рддрд░реНрдХ рдирд┐рдпрдореЛрдВ рдХрд╛ рдкрддрд╛ рд▓рдЧрд╛рдирд╛ред
  • рд╕реБрдирд┐рд╢реНрдЪрд┐рдд рдХрд░реЗрдВ рдХрд┐ рдХреБрдЫ рднреА рдЯреВрдЯрд╛ рд╣реБрдЖ рдирд╣реАрдВ рд╣реИред

рдпрд╣ рдПрдХ рдЦрд░рд╛рдм рдХреЛрдб рдбрд┐рдЬрд╝рд╛рдЗрди рдХреЗ рд▓рдХреНрд╖рдг рдХреА рддрд░рд╣ рд▓рдЧ рд╕рдХрддрд╛ рд╣реИ, рд▓реЗрдХрд┐рди рдпрд╣реА рдмрд╛рдд рд╢рд╛рд▓реАрдирддрд╛ рд╕реЗ рд▓рд┐рдЦреЗ рдЧрдП рдкреНрд░реЛрдЬреЗрдХреНрдЯреЛрдВ рдкрд░ рднреА рд╣реЛрддреА рд╣реИред
рдареАрдХ рд╣реИ, рд▓реЗрдХрд┐рди рд╣рдо C # рдореЗрдВ рд╕рдорд╛рди Result рдЙрдкрдпреЛрдЧ рдХрд░рдиреЗ рдХрд╛ рдкреНрд░рдпрд╛рд╕ рдХрд░ рд╕рдХрддреЗ рд╣реИрдВред рд╕рдмрд╕реЗ рд╕реНрдкрд╖реНрдЯ рдХрд╛рд░реНрдпрд╛рдиреНрд╡рдпрди рдЗрд╕ рддрд░рд╣ рджрд┐рдЦреЗрдЧрд╛:


 public class Result<TOk, TError> { public TOk Ok {get;set;} public TError Error {get;set;} } 

рдФрд░ рдпрд╣ рдПрдХ рд╢реБрджреНрдз рдХрдЪрд░рд╛ рд╣реИ, рдпрд╣ рд╣рдореЗрдВ Ok рдФрд░ Error рджреЛрдиреЛрдВ рдХреЛ рд╕реЗрдЯ рдХрд░рдиреЗ рд╕реЗ рдирд╣реАрдВ рд░реЛрдХрддрд╛ рд╣реИ рдФрд░ рддреНрд░реБрдЯрд┐ рдХреЛ рдкреВрд░реА рддрд░рд╣ рд╕реЗ рдирдЬрд░рдЕрдВрджрд╛рдЬ рдХрд░рдиреЗ рдХреА рдЕрдиреБрдорддрд┐ рджреЗрддрд╛ рд╣реИред рдЙрдЪрд┐рдд рд╕рдВрд╕реНрдХрд░рдг рдХреБрдЫ рдЗрд╕ рддрд░рд╣ рд╣реЛрдЧрд╛:


 public abstract class Result<TOk, TError> { public abstract bool IsOk { get; } private sealed class OkResult : Result<TOk, TError> { public readonly TOk _ok; public OkResult(TOk ok) { _ok = ok; } public override bool IsOk => true; } private sealed class ErrorResult : Result<TOk, TError> { public readonly TError _error; public ErrorResult(TError error) { _error = error; } public override bool IsOk => false; } public static Result<TOk, TError> Ok(TOk ok) => new OkResult(ok); public static Result<TOk, TError> Error(TError error) => new ErrorResult(error); public Result<T, TError> Map<T>(Func<TOk, T> map) { if (this.IsOk) { var value = ((OkResult)this)._ok; return Result<T, TError>.Ok(map(value)); } else { var value = ((ErrorResult)this)._error; return Result<T, TError>.Error(value); } } public Result<TOk, T> MapError<T>(Func<TError, T> mapError) { if (this.IsOk) { var value = ((OkResult)this)._ok; return Result<TOk, T>.Ok(value); } else { var value = ((ErrorResult)this)._error; return Result<TOk, T>.Error(mapError(value)); } } } 

рдмрд╣реБрдд рдмреЛрдЭрд┐рд▓, рд╕рд╣реА? рдФрд░ рдореИрдВрдиреЗ Map рдФрд░ MapError рдХреЗ void рд╕рдВрд╕реНрдХрд░рдгреЛрдВ рдХреЛ рднреА рд▓рд╛рдЧреВ рдирд╣реАрдВ рдХрд┐рдпрд╛ред рдЙрдкрдпреЛрдЧ рдЗрд╕ рддрд░рд╣ рджрд┐рдЦреЗрдЧрд╛:


 void Test(Result<int, string> result) { var squareResult = result.Map(x => x * x); } 

рдЗрддрдирд╛ рдмреБрд░рд╛ рдирд╣реАрдВ рд╣реИ, рдЙрд╣? рдареАрдХ рд╣реИ, рдЕрдм рдЖрдк рдХрд▓реНрдкрдирд╛ рдХрд░реЗрдВ рдХрд┐ рдЖрдкрдХреЗ рддреАрди рдкрд░рд┐рдгрд╛рдо рд╣реИрдВ рдФрд░ рдЖрдк рдЙрди рд╕рднреА рдХреЗ рд╕рд╛рде рдХреБрдЫ рдХрд░рдирд╛ рдЪрд╛рд╣рддреЗ рд╣реИрдВ, рдЬрдм рд╕рднреА Ok ред рдмреБрд░рд╛ред рддреЛ рд╡рд╣ рд╢рд╛рдпрдж рд╣реА рдХреЛрдИ рд╡рд┐рдХрд▓реНрдк рд╣реЛред
рдПрдл # рд╕рдВрд╕реНрдХрд░рдг:


 //-- this type is in standard library, but declaration looks like this: type Result<'ok, 'error> = | Ok of 'ok | Error of 'error //-- and usage: let test res1 res2 res3 = match res1, res2, res3 with | Ok ok1, Ok ok2, Ok ok3 -> printfn "1: %A 2: %A 3: %A" ok1 ok2 ok3 | _ -> printfn "fail" 

рдореВрд▓ рд░реВрдк рд╕реЗ, рдЖрдкрдХреЛ рдпрд╣ рдЪреБрдирдирд╛ рд╣реЛрдЧрд╛ рдХрд┐ рдЖрдк рдЙрдЪрд┐рдд рдорд╛рддреНрд░рд╛ рдореЗрдВ рдХреЛрдб рд▓рд┐рдЦрддреЗ рд╣реИрдВ, рд▓реЗрдХрд┐рди рдХреЛрдб рдЕрд╕реНрдкрд╖реНрдЯ рд╣реИ, рдЕрдкрд╡рд╛рджреЛрдВ, рдкреНрд░рддрд┐рдмрд┐рдВрдм, рдЕрднрд┐рд╡реНрдпрдХреНрддрд┐рдпреЛрдВ рдФрд░ рдЕрдиреНрдп "рдЬрд╛рджреВ" рдкрд░ рдирд┐рд░реНрднрд░ рдХрд░рддрд╛ рд╣реИ, рдпрд╛ рдЖрдк рдмрд╣реБрдд рдЕрдзрд┐рдХ рдХреЛрдб рд▓рд┐рдЦрддреЗ рд╣реИрдВ, рдЬреЛ рдкрдврд╝рдирд╛ рдореБрд╢реНрдХрд┐рд▓ рд╣реИ, рд▓реЗрдХрд┐рди рдпрд╣ рдЕрдзрд┐рдХ рдЯрд┐рдХрд╛рдК рд╣реИ рдФрд░ рд╕реАрдзреЗ рдЖрдЧреЗред рдЬрдм рдЗрд╕ рддрд░рд╣ рдХреА рдкрд░рд┐рдпреЛрдЬрдирд╛ рдмрдбрд╝реА рд╣реЛ рдЬрд╛рддреА рд╣реИ рддреЛ рдЖрдк рдЗрд╕рдХрд╛ рдореБрдХрд╛рдмрд▓рд╛ рдирд╣реАрдВ рдХрд░ рд╕рдХрддреЗ рд╣реИрдВ, рди рдХрд┐ рд╕реА # рдЬреИрд╕реА рдЯрд╛рдЗрдк рдкреНрд░рдгрд╛рд▓рд┐рдпреЛрдВ рд╡рд╛рд▓реЗ рднрд╛рд╖рд╛рдУрдВ рдореЗрдВред рдЖрдЗрдП рдПрдХ рд╕рд╛рдзрд╛рд░рдг рдкрд░рд┐рджреГрд╢реНрдп рдкрд░ рд╡рд┐рдЪрд╛рд░ рдХрд░реЗрдВ: рдЖрдкрдХреЗ рдкрд╛рд╕ рдХреЛрдбрдмрд╕реЗ рдореЗрдВ рдереЛрдбрд╝реА рджреЗрд░ рдХреЗ рд▓рд┐рдП рдХреБрдЫ рдЗрдХрд╛рдИ рд╣реИред рдЖрдЬ рдЖрдк рдПрдХ рдирдпрд╛ рдЖрд╡рд╢реНрдпрдХ рдХреНрд╖реЗрддреНрд░ рдЬреЛрдбрд╝рдирд╛ рдЪрд╛рд╣рддреЗ рд╣реИрдВред рд╕реНрд╡рд╛рднрд╛рд╡рд┐рдХ рд░реВрдк рд╕реЗ рдЖрдкрдХреЛ рдЗрд╕ рдлрд╝реАрд▓реНрдб рдХреЛ рд╣рд░ рдЬрдЧрд╣ рдЗрдирд┐рд╢рд┐рдпрд▓рд╛рдЗрдЬрд╝ рдХрд░рдиреЗ рдХреА рдЖрд╡рд╢реНрдпрдХрддрд╛ рд╣реЛрддреА рд╣реИ, рд▓реЗрдХрд┐рди рдпрд╣ рдХрдВрдкрд╛рдЗрд▓рд░ рдЖрдкрдХреА рдорджрдж рдирд╣реАрдВ рдХрд░рддрд╛ рд╣реИ, рдХреНрдпреЛрдВрдХрд┐ рдХреНрд▓рд╛рд╕ рдореНрдпреВрдЯреЗрдмрд▓ рд╣реИ рдФрд░ null рдПрдХ рд╡реИрд▓реНрдпреВ рд╡реИрд▓реНрдпреВ рд╣реИред рдФрд░ AutoMapper рдЬреИрд╕реА рд▓рд╛рдЗрдмреНрд░реЗрд░реА рдЗрд╕реЗ рдФрд░ рднреА рдХрдард┐рди рдмрдирд╛рддреА рд╣реИрдВред рдпрд╣ рдкрд░рд┐рд╡рд░реНрддрдирд╢реАрд▓рддрд╛ рд╣рдореЗрдВ рд╡рд╕реНрддреБрдУрдВ рдХреЛ рдПрдХ рд╕реНрдерд╛рди рдкрд░ рдЖрдВрд╢рд┐рдХ рд░реВрдк рд╕реЗ рдЖрд░рдВрдн рдХрд░рдиреЗ рдХреА рдЕрдиреБрдорддрд┐ рджреЗрддреА рд╣реИ, рдлрд┐рд░ рдЗрд╕реЗ рдХрд╣реАрдВ рдФрд░ рдзрдХреЗрд▓рддреА рд╣реИ рдФрд░ рд╡рд╣рд╛рдВ рдЖрд░рдВрднреАрдХрд░рдг рдЬрд╛рд░реА рд░рдЦрддреА рд╣реИред рдпрд╣ рдХреАрдбрд╝реЗ рдХрд╛ рдПрдХ рдФрд░ рд╕реНрд░реЛрдд рд╣реИред


рдЗрд╕ рдмреАрдЪ рднрд╛рд╖рд╛ рдХреА рд╕реБрд╡рд┐рдзрд╛ рддреБрд▓рдирд╛ рдЕрдЪреНрдЫреА рд╣реИ, рд╣рд╛рд▓рд╛рдВрдХрд┐ рдпрд╣ рдЗрд╕ рд▓реЗрдЦ рдХреЗ рдмрд╛рд░реЗ рдореЗрдВ рдирд╣реАрдВ рд╣реИред рдпрджрд┐ рдЖрдк рдЗрд╕рдореЗрдВ рд░реБрдЪрд┐ рд░рдЦрддреЗ рд╣реИрдВ, рддреЛ рдореИрдВрдиреЗ рдЕрдкрдиреЗ рдкрд┐рдЫрд▓реЗ рд▓реЗрдЦ рдореЗрдВ рдЙрд╕ рд╡рд┐рд╖рдп рдХреЛ рд╢рд╛рдорд┐рд▓ рдХрд┐рдпрд╛ рд╣реИред рд▓реЗрдХрд┐рди рднрд╛рд╖рд╛ рдХреА рд╡рд┐рд╢реЗрд╖рддрд╛рдПрдВ рдЦреБрдж рдХреЛ рдкреНрд░реМрджреНрдпреЛрдЧрд┐рдХреА рд╕реНрд╡рд┐рдЪ рдХрд░рдиреЗ рдХрд╛ рдПрдХ рдХрд╛рд░рдг рдирд╣реАрдВ рд╣реЛрдирд╛ рдЪрд╛рд╣рд┐рдПред


рддрд╛рдХрд┐ рд╣рдореЗрдВ рдЗрди рд╕рд╡рд╛рд▓реЛрдВ рд╕реЗ рд░реБрдмрд░реБ рдХрд░рд╛рдпрд╛ рдЬрд╛рдП:


  1. рд╣рдореЗрдВ рд╡рд╛рд╕реНрддрд╡ рдореЗрдВ рдЖрдзреБрдирд┐рдХ рдУрдУрдкреА рд╕реЗ рд╕реНрд╡рд┐рдЪ рдХрд░рдиреЗ рдХреА рдЖрд╡рд╢реНрдпрдХрддрд╛ рдХреНрдпреЛрдВ рд╣реИ?
  2. рд╣рдореЗрдВ рдПрдлрдкреА рдкрд░ рд╕реНрд╡рд┐рдЪ рдХреНрдпреЛрдВ рдХрд░рдирд╛ рдЪрд╛рд╣рд┐рдП?

рдкрд╣рд▓реЗ рдкреНрд░рд╢реНрди рдХрд╛ рдЙрддреНрддрд░ рдЖрдзреБрдирд┐рдХ рдЕрдиреБрдкреНрд░рдпреЛрдЧреЛрдВ рдХреЗ рд▓рд┐рдП рдЖрдо рдУрдУрдкреА рднрд╛рд╖рд╛рдУрдВ рдХрд╛ рдЙрдкрдпреЛрдЧ рдХрд░ рд░рд╣рд╛ рд╣реИ, рдЬрд┐рд╕рд╕реЗ рдЖрдкрдХреЛ рдмрд╣реБрдд рдкрд░реЗрд╢рд╛рдиреА рд╣реЛрддреА рд╣реИ, рдХреНрдпреЛрдВрдХрд┐ рд╡реЗ рдПрдХ рдЕрд▓рдЧ рдЙрджреНрджреЗрд╢реНрдп рдХреЗ рд▓рд┐рдП рдбрд┐рдЬрд╝рд╛рдЗрди рдХрд┐рдП рдЧрдП рдереЗред рдпрд╣ рдЖрдкрдХреЗ рдЖрд╡реЗрджрди рдХреА рдЬрдЯрд┐рд▓рддрд╛ рд╕реЗ рд▓рдбрд╝рдиреЗ рдХреЗ рд╕рд╛рде-рд╕рд╛рде рдЙрдирдХреЗ рдбрд┐рдЬрд╛рдЗрди рд╕реЗ рд▓рдбрд╝рдиреЗ рдХреЗ рд▓рд┐рдП рдЖрдкрдХреЗ рджреНрд╡рд╛рд░рд╛ рдЦрд░реНрдЪ рдХрд┐рдП рдЧрдП рд╕рдордп рдФрд░ рдзрди рдХрд╛ рдкрд░рд┐рдгрд╛рдо рд╣реИред


рдФрд░ рджреВрд╕рд░рд╛ рдЬрд╡рд╛рдм рд╣реИ рдПрдлрдкреА рднрд╛рд╖рд╛рдПрдБ рдЖрдкрдХреЛ рдЕрдкрдиреА рд╕реБрд╡рд┐рдзрд╛рдУрдВ рдХреЛ рдбрд┐рдЬрд╝рд╛рдЗрди рдХрд░рдиреЗ рдХрд╛ рдПрдХ рдЖрд╕рд╛рди рддрд░реАрдХрд╛ рджреЗрддреА рд╣реИрдВ рддрд╛рдХрд┐ рд╡реЗ рдШрдбрд╝реА рдХреА рддрд░рд╣ рдХрд╛рдо рдХрд░реЗрдВ, рдФрд░ рдпрджрд┐ рдХреЛрдИ рдирдпрд╛ рдлреАрдЪрд░ рдореМрдЬреВрджрд╛ рддрд░реНрдХ рдХреЛ рддреЛрдбрд╝рддрд╛ рд╣реИ, рддреЛ рдпрд╣ рдХреЛрдб рдХреЛ рддреЛрдбрд╝ рджреЗрддрд╛ рд╣реИ, рдЗрд╕рд▓рд┐рдП рдЖрдкрдХреЛ рддреБрд░рдВрдд рдкрддрд╛ рдЪрд▓ рдЬрд╛рддрд╛ рд╣реИред




рд╣рд╛рд▓рд╛рдБрдХрд┐ рд╡реЗ рдЙрддреНрддрд░ рдкрд░реНрдпрд╛рдкреНрдд рдирд╣реАрдВ рд╣реИрдВред рдЬреИрд╕рд╛ рдХрд┐ рдореЗрд░реЗ рдорд┐рддреНрд░ рдиреЗ рд╣рдорд╛рд░реА рдЪрд░реНрдЪрд╛рдУрдВ рдореЗрдВ рд╕реЗ рдПрдХ рдХреЗ рджреМрд░рд╛рди рдмрддрд╛рдпрд╛, рдПрдлрдкреА рдкрд░ рд╕реНрд╡рд┐рдЪ рдХрд░рдирд╛ рддрдм рдмреЗрдХрд╛рд░ рд╣реЛрдЧрд╛ рдЬрдм рдЖрдк рд╕рд░реНрд╡реЛрддреНрддрдо рдкреНрд░рдерд╛рдУрдВ рдХреЛ рдирд╣реАрдВ рдЬрд╛рдирддреЗ рд╣реИрдВред рд╣рдорд╛рд░реЗ рдмрдбрд╝реЗ рдЙрджреНрдпреЛрдЧ рдиреЗ рдУрдУрдкреА рдЕрдиреБрдкреНрд░рдпреЛрдЧреЛрдВ рдХреЗ рдбрд┐рдЬрд╛рдЗрди рдХреЗ рдмрд╛рд░реЗ рдореЗрдВ рд▓реЗрдЦреЛрдВ, рдкреБрд╕реНрддрдХреЛрдВ рдФрд░ рдЯреНрдпреВрдЯреЛрд░рд┐рдпрд▓ рдХреЗ рдЯрди рдХрд╛ рдЙрддреНрдкрд╛рджрди рдХрд┐рдпрд╛, рдФрд░ рд╣рдорд╛рд░реЗ рдкрд╛рд╕ рдУрдУрдкреА рдХреЗ рд╕рд╛рде рдЙрддреНрдкрд╛рджрди рдХрд╛ рдЕрдиреБрднрд╡ рд╣реИ, рдЗрд╕рд▓рд┐рдП рд╣рдо рдЬрд╛рдирддреЗ рд╣реИрдВ рдХрд┐ рд╡рд┐рднрд┐рдиреНрди рджреГрд╖реНрдЯрд┐рдХреЛрдгреЛрдВ рд╕реЗ рдХреНрдпрд╛ рдЙрдореНрдореАрдж рд╣реИред рджреБрд░реНрднрд╛рдЧреНрдп рд╕реЗ, рдпрд╣ рдХрд╛рд░реНрдпрд╛рддреНрдордХ рдкреНрд░реЛрдЧреНрд░рд╛рдорд┐рдВрдЧ рдХреЗ рд▓рд┐рдП рдорд╛рдорд▓рд╛ рдирд╣реАрдВ рд╣реИ, рдЗрд╕рд▓рд┐рдП рдпрд╣рд╛рдВ рддрдХ тАЛтАЛрдХрд┐ рдЕрдЧрд░ рдЖрдк рдПрдлрдкреА рдкрд░ рд╕реНрд╡рд┐рдЪ рдХрд░рддреЗ рд╣реИрдВ, рддреЛ рдЖрдкрдХрд╛ рдкрд╣рд▓рд╛ рдкреНрд░рдпрд╛рд╕ рд╕рдмрд╕реЗ рдЕрдЬреАрдм рд╣реЛрдЧрд╛ рдФрд░ рдирд┐рд╢реНрдЪрд┐рдд рд░реВрдк рд╕реЗ рдЖрдкрдХреЛ рд╡рд╛рдВрдЫрд┐рдд рдкрд░рд┐рдгрд╛рдо рдирд╣реАрдВ рд▓рд╛рдПрдЧрд╛: рдЬрдЯрд┐рд▓ рд╕рд┐рд╕реНрдЯрдо рдХрд╛ рддреЗрдЬ рдФрд░ рджрд░реНрдж рд░рд╣рд┐рдд рд╡рд┐рдХрд╛рд╕ред


рдЦреИрд░, рдпрд╣ рдареАрдХ рд╣реИ рдХрд┐ рдпрд╣ рд▓реЗрдЦ рдХрд┐рд╕ рдмрд╛рд░реЗ рдореЗрдВ рд╣реИред рдЬреИрд╕рд╛ рдХрд┐ рдореИрдВрдиреЗ рдХрд╣рд╛, рд╣рдо рдЕрдВрддрд░ рджреЗрдЦрдиреЗ рдХреЗ рд▓рд┐рдП рдкреНрд░реЛрдбрдХреНрд╢рди рдЬреИрд╕реЗ рдПрдкреНрд▓рд┐рдХреЗрд╢рди рдХрд╛ рдирд┐рд░реНрдорд╛рдг рдХрд░рдиреЗ рдЬрд╛ рд░рд╣реЗ рд╣реИрдВред


рд╣рдо рдЖрд╡реЗрджрди рдХреИрд╕реЗ рдбрд┐рдЬрд╛рдЗрди рдХрд░рддреЗ рд╣реИрдВ?


рдЗрд╕ рддрд░рд╣ рдХреЗ рдмрд╣реБрдд рд╕рд╛рд░реЗ рд╡рд┐рдЪрд╛рд░ рдЬреЛ рдореИрдВрдиреЗ рдбрд┐рдЬрд╛рдЗрди рдХреА рдкреНрд░рдХреНрд░рд┐рдпрд╛ рдореЗрдВ рдЙрдкрдпреЛрдЧ рдХрд┐рдП рдереЗ, рдореИрдВрдиреЗ рдорд╣рд╛рди рдкреБрд╕реНрддрдХ рдбреЛрдореЗрди рдореЙрдбрд▓рд┐рдВрдЧ рдореЗрдб рдлрдВрдХреНрд╢рдирд▓ рд╕реЗ рдЙрдзрд╛рд░ рд▓рд┐рдпрд╛ рдерд╛, рдЗрд╕рд▓рд┐рдП рдореИрдВ рдЖрдкрдХреЛ рдЗрд╕реЗ рдкрдврд╝рдиреЗ рдХреЗ рд▓рд┐рдП рджреГрдврд╝рддрд╛ рд╕реЗ рдкреНрд░реЛрддреНрд╕рд╛рд╣рд┐рдд рдХрд░рддрд╛ рд╣реВрдВред


рдЯрд┐рдкреНрдкрдгрд┐рдпреЛрдВ рдХреЗ рд╕рд╛рде рдкреВрд░реНрдг рд╕реНрд░реЛрдд рдХреЛрдб рдпрд╣рд╛рдБ рд╣реИ ред рд╕реНрд╡рд╛рднрд╛рд╡рд┐рдХ рд░реВрдк рд╕реЗ, рдореИрдВ рдпрд╣ рд╕рдм рдпрд╣рд╛рдБ рдирд╣реАрдВ рдХрд░рдиреЗ рдЬрд╛ рд░рд╣рд╛ рд╣реВрдБ, рдЗрд╕рд▓рд┐рдП рдореИрдВ рд╕рд┐рд░реНрдл рдореБрдЦреНрдп рдмрд┐рдВрджреБрдУрдВ рдкрд░ рдЪрд▓реВрдБрдЧрд╛ред


рд╣рдорд╛рд░реЗ рдкрд╛рд╕ 4 рдореБрдЦреНрдп рдкрд░рд┐рдпреЛрдЬрдирд╛рдПрдБ рд╣реЛрдВрдЧреА: рд╡реНрдпрд╛рд╡рд╕рд╛рдпрд┐рдХ рдкрд░рдд, рдбреЗрдЯрд╛ рдПрдХреНрд╕реЗрд╕ рдкрд░рдд, рдмреБрдирд┐рдпрд╛рджреА рдврд╛рдВрдЪрд╛ рдФрд░, рдЬрд╝рд╛рд╣рд┐рд░ рд╣реИ, рдЖрдоред рд╣рд░ рд╕рдорд╛рдзрд╛рди рд╣реИ, рд╣реИ рдирд╛?


рд╣рдо рдЕрдкрдиреЗ рдбреЛрдореЗрди рдХреА рдореЙрдбрд▓рд┐рдВрдЧ рд╕реЗ рд╢реБрд░реБрдЖрдд рдХрд░рддреЗ рд╣реИрдВред рдЗрд╕ рдмрд┐рдВрджреБ рдкрд░ рд╣рдо рдирд╣реАрдВ рдЬрд╛рдирддреЗ рд╣реИрдВ рдФрд░ рдбреЗрдЯрд╛рдмреЗрд╕ рдХреА рдкрд░рд╡рд╛рд╣ рдирд╣реАрдВ рдХрд░рддреЗ рд╣реИрдВред рдпрд╣ рдЙрджреНрджреЗрд╢реНрдп рдкрд░ рдХрд┐рдпрд╛ рдЧрдпрд╛ рд╣реИ, рдХреНрдпреЛрдВрдХрд┐ рд╡рд┐рд╢рд┐рд╖реНрдЯ рдбреЗрдЯрд╛рдмреЗрд╕ рдХреЛ рдзреНрдпрд╛рди рдореЗрдВ рд░рдЦрддреЗ рд╣реБрдП рд╣рдо рдЕрдкрдиреЗ рдбреЛрдореЗрди рдХреЛ рдЗрд╕рдХреЗ рдЕрдиреБрд╕рд╛рд░ рдбрд┐рдЬрд╝рд╛рдЗрди рдХрд░рддреЗ рд╣реИрдВ, рд╣рдо рдЗрд╕ рдЗрдХрд╛рдИ-рддрд╛рд▓рд┐рдХрд╛ рд╕рдВрдмрдВрдз рдХреЛ рд╡реНрдпрд╛рд╡рд╕рд╛рдпрд┐рдХ рдкрд░рдд рдореЗрдВ рд▓рд╛рддреЗ рд╣реИрдВ, рдЬреЛ рдмрд╛рдж рдореЗрдВ рд╕рдорд╕реНрдпрд╛рдПрдВ рд▓рд╛рддрд╛ рд╣реИред рдЖрдкрдХреЛ рдХреЗрд╡рд▓ рдореИрдкрд┐рдВрдЧ domain -> DAL рд▓рд╛рдЧреВ рдХрд░рдиреЗ рдХреА рдЖрд╡рд╢реНрдпрдХрддрд╛ рд╣реИ domain -> DAL рдПрдХ рдмрд╛рд░, рдЬрдмрдХрд┐ рдЧрд▓рдд рдбрд┐рдЬрд╝рд╛рдЗрди рд╣рдореЗрдВ рд▓рдЧрд╛рддрд╛рд░ рдкрд░реЗрд╢рд╛рди рдХрд░реЗрдЧрд╛ рдЬрдм рддрдХ рдХрд┐ рд╣рдо рдЗрд╕реЗ рдареАрдХ рдирд╣реАрдВ рдХрд░ рд▓реЗрддреЗред рддреЛ рдпрд╣рд╛рдБ рд╣рдо рдХреНрдпрд╛ рдХрд░рддреЗ рд╣реИрдВ: рд╣рдо CardManagement (рдмрд╣реБрдд рд░рдЪрдирд╛рддреНрдордХ, рдореБрдЭреЗ рдкрддрд╛ рд╣реИ) рдирд╛рдордХ рдПрдХ рдкреНрд░реЛрдЬреЗрдХреНрдЯ рдмрдирд╛рддреЗ рд╣реИрдВ, рдФрд░ рддреБрд░рдВрдд рдкреНрд░реЛрдЬреЗрдХреНрдЯ рдлрд╝рд╛рдЗрд▓ рдореЗрдВ <TreatWarningsAsErrors>true</TreatWarningsAsErrors> рд╕реЗрдЯрд┐рдВрдЧ рдЪрд╛рд▓реВ рдХрд░рддреЗ рд╣реИрдВред рд╣рдореЗрдВ рдЗрд╕рдХреА рдЖрд╡рд╢реНрдпрдХрддрд╛ рдХреНрдпреЛрдВ рд╣реИ? рдареАрдХ рд╣реИ, рд╣рдо рд╡рд┐рд╡реЗрдХрд╣реАрди рд╕рдВрдШреЛрдВ рдХрд╛ рднрд╛рд░реА рдЙрдкрдпреЛрдЧ рдХрд░рдиреЗ рдЬрд╛ рд░рд╣реЗ рд╣реИрдВ, рдФрд░ рдЬрдм рдЖрдк рдкреИрдЯрд░реНрди рдорд┐рд▓рд╛рди рдХрд░рддреЗ рд╣реИрдВ, рддреЛ рдХрдВрдкрд╛рдЗрд▓рд░ рд╣рдореЗрдВ рдПрдХ рдЪреЗрддрд╛рд╡рдиреА рджреЗрддрд╛ рд╣реИ, рдЕрдЧрд░ рд╣рдо рд╕рднреА рд╕рдВрднрд╛рд╡рд┐рдд рдорд╛рдорд▓реЛрдВ рдХреЛ рдХрд╡рд░ рдирд╣реАрдВ рдХрд░рддреЗ рд╣реИрдВ:


 let fail result = match result with | Ok v -> printfn "%A" v //-- warning: Incomplete pattern matches on this expression. //-- For example, the value 'Error' may indicate a case not covered by the pattern(s). 

рдЗрд╕ рд╕реЗрдЯрд┐рдВрдЧ рдХреЗ рд╕рд╛рде, рдпрд╣ рдХреЛрдб рдЕрднреА рд╕рдВрдХрд▓рд┐рдд рдирд╣реАрдВ рд╣реЛрдЧрд╛, рдЬреЛ рдХрд┐ рд╡рд╛рд╕реНрддрд╡ рдореЗрдВ рд╣рдорд╛рд░реА рдЬрд░реВрд░рдд рд╣реИ, рдЬрдм рд╣рдо рдореМрдЬреВрджрд╛ рдХрд╛рд░реНрдпрдХреНрд╖рдорддрд╛ рдХрд╛ рд╡рд┐рд╕реНрддрд╛рд░ рдХрд░рддреЗ рд╣реИрдВ рдФрд░ рдЪрд╛рд╣рддреЗ рд╣реИрдВ рдХрд┐ рдЗрд╕реЗ рд╣рд░ рдЬрдЧрд╣ рд╕рдорд╛рдпреЛрдЬрд┐рдд рдХрд┐рдпрд╛ рдЬрд╛рдПред рдЕрдЧрд▓реА рдЪреАрдЬ рдЬреЛ рд╣рдо рдХрд░рддреЗ рд╣реИрдВ рд╡рд╣ рд╣реИ рдореЙрдбреНрдпреВрд▓ рдмрдирд╛рдирд╛ (рдпрд╣ рдПрдХ рд╕реНрдерд┐рд░ рд╡рд░реНрдЧ рдореЗрдВ рд╕рдВрдХрд▓рд┐рдд рдХрд░рддрд╛ рд╣реИ) CardDomain ред рдЗрд╕ рдлрд╝рд╛рдЗрд▓ рдореЗрдВ рд╣рдо рдбреЛрдореЗрди рдкреНрд░рдХрд╛рд░реЛрдВ рдХрд╛ рд╡рд░реНрдгрди рдХрд░рддреЗ рд╣реИрдВ рдФрд░ рдЗрд╕рд╕реЗ рдЕрдзрд┐рдХ рдХреБрдЫ рдирд╣реАрдВред рдзреНрдпрд╛рди рд░рдЦреЗрдВ рдХрд┐ рдПрдл # рдореЗрдВ, рдХреЛрдб рдФрд░ рдлрд╛рдЗрд▓ рдСрд░реНрдбрд░ рдХреЗ рдорд╛рдорд▓реЗ: рдбрд┐рдлрд╝реЙрд▓реНрдЯ рд░реВрдк рд╕реЗ рдЖрдк рдХреЗрд╡рд▓ рд╡рд╣реА рдЙрдкрдпреЛрдЧ рдХрд░ рд╕рдХрддреЗ рд╣реИрдВ рдЬреЛ рдЖрдкрдиреЗ рдкрд╣рд▓реЗ рдШреЛрд╖рд┐рдд рдХрд┐рдпрд╛ рдерд╛ред


рдбреЛрдореЗрди рдкреНрд░рдХрд╛рд░


рд╣рдо CardNumber рд╕рд╛рде рд╣рдорд╛рд░реЗ рдкреНрд░рдХрд╛рд░реЛрдВ рдХреЛ рдкрд░рд┐рднрд╛рд╖рд┐рдд рдХрд░рдирд╛ рд╢реБрд░реВ рдХрд░рддреЗ рд╣реИрдВ, CardNumber рдореИрдВрдиреЗ рдкрд╣рд▓реЗ рджрд┐рдЦрд╛рдпрд╛ рдерд╛, рд╣рд╛рд▓рд╛рдВрдХрд┐ рд╣рдореЗрдВ рдмрд╕ рдПрдХ рд╕реНрдЯреНрд░рд┐рдВрдЧ рдХреА рддреБрд▓рдирд╛ рдореЗрдВ рдЕрдзрд┐рдХ рд╡реНрдпрд╛рд╡рд╣рд╛рд░рд┐рдХ Error рдЖрд╡рд╢реНрдпрдХрддрд╛ рд╣реИ, рдЗрд╕рд▓рд┐рдП рд╣рдо ValidationError рдЙрдкрдпреЛрдЧ рдХрд░реЗрдВрдЧреЗред


 type ValidationError = { FieldPath: string Message: string } let validationError field message = { FieldPath = field; Message = message } (*{- Actually we should use here Luhn's algorithm, but I leave it to you as an exercise, so you can see for yourself how easy is updating code to new requirements. -}*) let private cardNumberRegex = new Regex("^[0-9]{16}$", RegexOptions.Compiled) type CardNumber = private CardNumber of string with member this.Value = match this with CardNumber s -> s static member create fieldName str = match str with | (null|"") -> validationError fieldName "card number can't be empty" | str -> if cardNumberRegex.IsMatch(str) then CardNumber str |> Ok else validationError fieldName "Card number must be a 16 digits string" 

рдлрд┐рд░ рд╣рдо рдирд┐рд╢реНрдЪрд┐рдд рд░реВрдк рд╕реЗ Card рдХреЛ рдкрд░рд┐рднрд╛рд╖рд┐рдд рдХрд░рддреЗ рд╣реИрдВ рдЬреЛ рд╣рдорд╛рд░реЗ рдбреЛрдореЗрди рдХрд╛ рджрд┐рд▓ рд╣реИред рд╣рдо рдЬрд╛рдирддреЗ рд╣реИрдВ рдХрд┐ рдХрд╛рд░реНрдб рдореЗрдВ рдХреБрдЫ рд╕реНрдерд╛рдпреА рдЧреБрдг рдЬреИрд╕реЗ рд╕рдВрдЦреНрдпрд╛, рд╕рдорд╛рдкреНрддрд┐ рддрд┐рдерд┐ рдФрд░ рдХрд╛рд░реНрдб рдкрд░ рдирд╛рдо рдФрд░ рд╢реЗрд╖ рдФрд░ рджреИрдирд┐рдХ рд╕реАрдорд╛ рдЬреИрд╕реА рдХреБрдЫ рдкрд░рд┐рд╡рд░реНрддрдирд╢реАрд▓ рдЬрд╛рдирдХрд╛рд░реА рд╣реЛрддреА рд╣реИ, рдЗрд╕рд▓рд┐рдП рд╣рдо рдЙрд╕ рдкрд░рд┐рд╡рд░реНрддрдирд╢реАрд▓ рдЬрд╛рдирдХрд╛рд░реА рдХреЛ рдЕрдиреНрдп рдкреНрд░рдХрд╛рд░ рдореЗрдВ рд╕рдВрд▓рдЧреНрди рдХрд░рддреЗ рд╣реИрдВ:


 type AccountInfo = { HolderId: UserId Balance: Money DailyLimit: DailyLimit } type Card = { CardNumber: CardNumber Name: LetterString HolderId: UserId Expiration: (Month * Year) AccountDetails: CardAccountInfo } 

рдЕрдм, рдпрд╣рд╛рдБ рдХрдИ рдкреНрд░рдХрд╛рд░ рд╣реИрдВ, рдЬрд┐рдиреНрд╣реЗрдВ рд╣рдордиреЗ рдЕрднреА рддрдХ рдШреЛрд╖рд┐рдд рдирд╣реАрдВ рдХрд┐рдпрд╛ рд╣реИ:


  1. рдкреИрд╕рд╛


    рд╣рдо decimal рдЙрдкрдпреЛрдЧ рдХрд░ рд╕рдХрддреЗ рд╣реИрдВ (рдФрд░ рд╣рдо рдХрд░реЗрдВрдЧреЗ, рд▓реЗрдХрд┐рди рд╕реАрдзреЗ рдирд╣реАрдВ), рд▓реЗрдХрд┐рди decimal рдХрдо рд╡рд░реНрдгрдирд╛рддреНрдордХ рд╣реИред рдЗрд╕рдХреЗ рдЕрд▓рд╛рд╡рд╛, рдпрд╣ рдкреИрд╕реЗ рдХреЗ рдЕрд▓рд╛рд╡рд╛ рдЕрдиреНрдп рдЪреАрдЬреЛрдВ рдХреЗ рдкреНрд░рддрд┐рдирд┐рдзрд┐рддреНрд╡ рдХреЗ рд▓рд┐рдП рдЗрд╕реНрддреЗрдорд╛рд▓ рдХрд┐рдпрд╛ рдЬрд╛ рд╕рдХрддрд╛ рд╣реИ, рдФрд░ рд╣рдо рдЗрд╕реЗ рдорд┐рд╢реНрд░рд┐рдд рдирд╣реАрдВ рдХрд░рдирд╛ рдЪрд╛рд╣рддреЗ рд╣реИрдВред рдЗрд╕рд▓рд┐рдП рд╣рдо рдХрд╕реНрдЯрдо рдкреНрд░рдХрд╛рд░ рдХреЗ type [<Struct>] Money = Money of decimal рдЙрдкрдпреЛрдЧ рдХрд░рддреЗ рд╣реИрдВ type [<Struct>] Money = Money of decimal ред


  2. DailyLimit


    рджреИрдирд┐рдХ рд╕реАрдорд╛ рдпрд╛ рддреЛ рдПрдХ рд╡рд┐рд╢рд┐рд╖реНрдЯ рд░рд╛рд╢рд┐ рдкрд░ рд╕реЗрдЯ рдХреА рдЬрд╛ рд╕рдХрддреА рд╣реИ рдпрд╛ рдмрд┐рд▓реНрдХреБрд▓ рдЕрдиреБрдкрд╕реНрдерд┐рдд рд╣реЛ рд╕рдХрддреА рд╣реИред рдпрджрд┐ рдпрд╣ рдореМрдЬреВрдж рд╣реИ, рддреЛ рдпрд╣ рд╕рдХрд╛рд░рд╛рддреНрдордХ рд╣реЛрдирд╛ рдЪрд╛рд╣рд┐рдПред decimal рдпрд╛ Money рдХрд╛ рдЙрдкрдпреЛрдЧ рдХрд░рдиреЗ рдХреЗ рдмрдЬрд╛рдп рд╣рдо рдЗрд╕ рдкреНрд░рдХрд╛рд░ рдХреЛ рдкрд░рд┐рднрд╛рд╖рд┐рдд рдХрд░рддреЗ рд╣реИрдВ:


     [<Struct>] type DailyLimit = private //-- private constructor so it can't be created directly outside of module | Limit of Money | Unlimited with static member ofDecimal dec = if dec > 0m then Money dec |> Limit else Unlimited member this.ToDecimalOption() = match this with | Unlimited -> None | Limit limit -> Some limit.Value 

    рдпрд╣ рдХреЗрд╡рд▓ рд╡рд┐рд╡рд░рдг рджреЗрдиреЗ рд╕реЗ рдЕрдзрд┐рдХ рд╡рд░реНрдгрдирд╛рддреНрдордХ рд╣реИ рдХрд┐ 0M рдорддрд▓рдм рд╣реИ рдХрд┐ рдЗрд╕рдХреА рдХреЛрдИ рд╕реАрдорд╛ рдирд╣реАрдВ рд╣реИ, рдХреНрдпреЛрдВрдХрд┐ рдЗрд╕рдХрд╛ рдорддрд▓рдм рдпрд╣ рднреА рд╣реЛ рд╕рдХрддрд╛ рд╣реИ рдХрд┐ рдЖрдк рдЗрд╕ рдХрд╛рд░реНрдб рдкрд░ рдкреИрд╕рд╛ рдЦрд░реНрдЪ рдирд╣реАрдВ рдХрд░ рд╕рдХрддреЗред рдПрдХрдорд╛рддреНрд░ рд╕рдорд╕реНрдпрд╛ рдпрд╣ рд╣реИ рдХрд┐ рд╣рдордиреЗ рдХрдВрд╕реНрдЯреНрд░рдХреНрдЯрд░ рдХреЛ рдЫрд┐рдкрд╛ рджрд┐рдпрд╛ рд╣реИ, рд╣рдо рдкреИрдЯрд░реНрди рдорд┐рд▓рд╛рди рдирд╣реАрдВ рдХрд░ рд╕рдХрддреЗред рд▓реЗрдХрд┐рди рдХреЛрдИ рдЪрд┐рдВрддрд╛ рдирд╣реАрдВ, рд╣рдо рд╕рдХреНрд░рд┐рдп рдкреИрдЯрд░реНрди рдХрд╛ рдЙрдкрдпреЛрдЧ рдХрд░ рд╕рдХрддреЗ рд╣реИрдВ:


     let (|Limit|Unlimited|) limit = match limit with | Limit dec -> Limit dec | Unlimited -> Unlimited 

    рдЕрдм рд╣рдо DailyLimit рд╣рд░ рдЬрдЧрд╣ рд░реЗрдЧреБрд▓рд░ рдбреАрдпреВ рдХреЗ рд░реВрдк рдореЗрдВ рдореИрдЪ рдХрд░ рд╕рдХрддреЗ рд╣реИрдВред


  3. LetterString


    рд╡рд╣ рд╕рд░рд▓ рд╣реИред рд╣рдо CardNumber рдореЗрдВ рдЙрд╕реА рддрдХрдиреАрдХ рдХрд╛ рдЙрдкрдпреЛрдЧ рдХрд░рддреЗ рд╣реИрдВред рд╣рд╛рд▓рд╛рдВрдХрд┐ рдПрдХ рдЫреЛрдЯреА рд╕реА рдмрд╛рдд: LetterString рдХреНрд░реЗрдбрд┐рдЯ рдХрд╛рд░реНрдб рдХреЗ рдмрд╛рд░реЗ рдореЗрдВ рд╢рд╛рдпрдж рд╣реА рд╣реЛ, рдпрд╣ рдПрдХ рдЕрдЪреНрдЫреА рдмрд╛рдд рд╣реИ рдФрд░ рд╣рдореЗрдВ рдЗрд╕реЗ CommonTypes рдореЙрдбреНрдпреВрд▓ рдореЗрдВ Common рдкреНрд░реЛрдЬреЗрдХреНрдЯ рдореЗрдВ рд╕реНрдерд╛рдирд╛рдВрддрд░рд┐рдд рдХрд░рдирд╛ рдЪрд╛рд╣рд┐рдПред рд╕рдордп рдЖрддрд╛ рд╣реИ рд╣рдо ValidationError рдХреЛ рдЕрд▓рдЧ рдЬрдЧрд╣ рдкрд░ рд╕реНрдерд╛рдирд╛рдВрддрд░рд┐рдд рдХрд░рддреЗ рд╣реИрдВред


  4. рдкреНрд░рдпреЛрдХреНрддрд╛-рдЖрдИрдбреА


    рд╡рд╣ рдХреЗрд╡рд▓ рдПрдХ рдЙрдкрдирд╛рдо type UserId = System.Guid ред рд╣рдо рдЗрд╕реЗ рдХреЗрд╡рд▓ рд╡рд░реНрдгрдирд╛рддреНрдордХрддрд╛ рдХреЗ рд▓рд┐рдП рдЙрдкрдпреЛрдЧ рдХрд░рддреЗ рд╣реИрдВред


  5. рдорд╣реАрдирд╛ рдФрд░ рд╕рд╛рд▓


    рдЬрд┐рдиреНрд╣реЗрдВ Common рднреА рдЬрд╛рдирд╛ рд╣реИред Month рдХреЛ рднреЗрджрднрд╛рд╡ рд░рд╣рд┐рдд рд╕рдВрдШ рдмрдирд╛рдиреЗ рдХреЗ рд▓рд┐рдП рд╣реИ рдФрд░ рдЗрд╕реЗ unsigned int16 рд╕реЗ рдмрджрд▓рдиреЗ рдХреЗ рд▓рд┐рдП, Year CardNumber рддрд░рд╣ рд╣реЛрдиреЗ рдЬрд╛ рд░рд╣рд╛ рд╣реИ рд▓реЗрдХрд┐рди рд╕реНрдЯреНрд░рд┐рдВрдЧ рдХреЗ рдмрдЬрд╛рдп uint16 рд▓рд┐рдПред



рдЕрдм рдЕрдкрдиреЗ рдбреЛрдореЗрди рдкреНрд░рдХрд╛рд░ рдХреА рдШреЛрд╖рдгрд╛ рдХреЛ рдкреВрд░рд╛ рдХрд░рддреЗ рд╣реИрдВред рд╣рдореЗрдВ рдХреБрдЫ рдЙрдкрдпреЛрдЧрдХрд░реНрддрд╛ рдЬрд╛рдирдХрд╛рд░реА рдФрд░ рдХрд╛рд░реНрдб рд╕рдВрдЧреНрд░рд╣ рдХреЗ рд╕рд╛рде рдЙрдкрдпреЛрдЧрдХрд░реНрддрд╛ рдХреА рдЖрд╡рд╢реНрдпрдХрддрд╛ рд╣реИ, рд╣рдореЗрдВ рдЯреЙрдк-рдЕрдк рдФрд░ рднреБрдЧрддрд╛рди рдХреЗ рд▓рд┐рдП рд╕рдВрддреБрд▓рди рд╕рдВрдЪрд╛рд▓рди рдХреА рдЖрд╡рд╢реНрдпрдХрддрд╛ рд╣реИред


  type UserInfo = { Name: LetterString Id: UserId Address: Address } type User = { UserInfo : UserInfo Cards: Card list } [<Struct>] type BalanceChange = //-- another common type with validation for positive amount | Increase of increase: MoneyTransaction | Decrease of decrease: MoneyTransaction with member this.ToDecimal() = match this with | Increase i -> i.Value | Decrease d -> -d.Value [<Struct>] type BalanceOperation = { CardNumber: CardNumber Timestamp: DateTimeOffset BalanceChange: BalanceChange NewBalance: Money } 

рдЕрдЪреНрдЫрд╛ рд╣реИ, рд╣рдордиреЗ рдЕрдкрдиреЗ рдкреНрд░рдХрд╛рд░реЛрдВ рдХреЛ рдЗрд╕ рддрд░рд╣ рд╕реЗ рдбрд┐рдЬрд╝рд╛рдЗрди рдХрд┐рдпрд╛ рд╣реИ рдХрд┐ рдЕрдорд╛рдиреНрдп рд░рд╛рдЬреНрдп рдЕрдкреНрд░рд╛рдкреНрдп рд╣реИред рдЕрдм рдЬрдм рднреА рд╣рдо рдЗрдирдореЗрдВ рд╕реЗ рдХрд┐рд╕реА рднреА рдкреНрд░рдХрд╛рд░ рдХреЗ рдЙрджрд╛рд╣рд░рдг рдХреЗ рд╕рд╛рде рд╕реМрджрд╛ рдХрд░рддреЗ рд╣реИрдВ рддреЛ рд╣рдореЗрдВ рдпрдХреАрди рд╣реИ рдХрд┐ рд╡рд╣рд╛рдБ рдбреЗрдЯрд╛ рдорд╛рдиреНрдп рд╣реИ рдФрд░ рд╣рдореЗрдВ рдЗрд╕реЗ рдлрд┐рд░ рд╕реЗ рдорд╛рдиреНрдп рдирд╣реАрдВ рдХрд░рдирд╛ рд╣реИред рдЕрдм рд╣рдо рд╡реНрдпрд╛рдкрд╛рд░ рддрд░реНрдХ рдХреЗ рд▓рд┐рдП рдЖрдЧреЗ рдмрдврд╝ рд╕рдХрддреЗ рд╣реИрдВ!


рд╡реНрдпрд╛рдкрд╛рд░ рддрд░реНрдХ


рд╣рдорд╛рд░реЗ рдпрд╣рд╛рдБ рдПрдХ рдЕрдЯреВрдЯ рдирд┐рдпрдо рд╣реЛрдЧрд╛: рд╕рднреА рд╡реНрдпрд╛рд╡рд╕рд╛рдпрд┐рдХ рддрд░реНрдХ рдХреЛ рд╢реБрджреНрдз рдХрд╛рд░реНрдпреЛрдВ рдореЗрдВ рдХреВрдЯрдмрджреНрдз рдХрд┐рдпрд╛ рдЬрд╛рдПрдЧрд╛ред рд╢реБрджреНрдз рдХрд╛рд░реНрдп рдПрдХ рдРрд╕рд╛ рдХрд╛рд░реНрдп рд╣реИ рдЬреЛ рдирд┐рдореНрди рдорд╛рдирджрдВрдбреЛрдВ рдХреЛ рдкреВрд░рд╛ рдХрд░рддрд╛ рд╣реИ:


  • рдХреЗрд╡рд▓ рдПрдХ рдЪреАрдЬ рдЬреЛ рдЖрдЙрдЯрдкреБрдЯ рдореВрд▓реНрдп рдХреА рдЧрдгрдирд╛ рдХрд░рддреА рд╣реИред рдЗрд╕рдХрд╛ рдХреЛрдИ рд╕рд╛рдЗрдб рдЗрдлреЗрдХреНрдЯ рдмрд┐рд▓реНрдХреБрд▓ рдирд╣реАрдВ рд╣реИред
  • рдпрд╣ рд╣рдореЗрд╢рд╛ рдПрдХ рд╣реА рдЗрдирдкреБрдЯ рдХреЗ рд▓рд┐рдП рдПрдХ рд╣реА рдЖрдЙрдЯрдкреБрдЯ рдХрд╛ рдЙрддреНрдкрд╛рджрди рдХрд░рддрд╛ рд╣реИред

рдЗрд╕рд▓рд┐рдП рд╢реБрджреНрдз рдХрд╛рд░реНрдп рдЕрдкрд╡рд╛рджреЛрдВ рдХреЛ рдирд╣реАрдВ рдлреЗрдВрдХрддреЗ рд╣реИрдВ, рдпрд╛рджреГрдЪреНрдЫрд┐рдХ рдореВрд▓реНрдпреЛрдВ рдХрд╛ рдЙрддреНрдкрд╛рджрди рдирд╣реАрдВ рдХрд░рддреЗ рд╣реИрдВ, рдХрд┐рд╕реА рднреА рд░реВрдк рдореЗрдВ рдмрд╛рд╣рд░реА рджреБрдирд┐рдпрд╛ рдХреЗ рд╕рд╛рде рдмрд╛рддрдЪреАрдд рдирд╣реАрдВ рдХрд░рддреЗ рд╣реИрдВ, рдпрд╣ рдбреЗрдЯрд╛рдмреЗрд╕ рдпрд╛ рдПрдХ рд╕рд░рд▓ DateTime.Now ред рдЕрдм рдХреИрд╕реЗред рдмреЗрд╢рдХ рдЕрд╢реБрджреНрдз рд╕рдорд╛рд░реЛрд╣ рдХреЗ рд╕рд╛рде рдмрд╛рддрдЪреАрдд рд╕реНрд╡рдЪрд╛рд▓рд┐рдд рд░реВрдк рд╕реЗ рдлрд╝рдВрдХреНрд╢рди рдЕрд╢реБрджреНрдз рдХреЛ рдкреНрд░рд╕реНрддреБрдд рдХрд░рддрд╛ рд╣реИред рддреЛ рд╣рдо рдХреНрдпрд╛ рд▓рд╛рдЧреВ рдХрд░реЗрдВрдЧреЗ?


рдпрд╣рд╛рдВ рд╣рдорд╛рд░реЗ рдкрд╛рд╕ рдЖрд╡рд╢реНрдпрдХрддрд╛рдУрдВ рдХреА рдПрдХ рд╕реВрдЪреА рд╣реИ:


  • рдХрд╛рд░реНрдб рдХреЛ рд╕рдХреНрд░рд┐рдп / рдирд┐рд╖реНрдХреНрд░рд┐рдп рдХрд░реЗрдВ


  • рдкреНрд░рдХреНрд░рд┐рдпрд╛ рднреБрдЧрддрд╛рди


    рд╣рдо рднреБрдЧрддрд╛рди рдХреА рдкреНрд░рдХреНрд░рд┐рдпрд╛ рдХрд░ рд╕рдХрддреЗ рд╣реИрдВ рдпрджрд┐:


    1. рдХрд╛рд░реНрдб рдХреА рд╕рдордп рд╕реАрдорд╛ рд╕рдорд╛рдкреНрдд рдирд╣реАрдВ рд╣реБрдИ рд╣реИ
    2. рдХрд╛рд░реНрдб рд╕рдХреНрд░рд┐рдп рд╣реИ
    3. рднреБрдЧрддрд╛рди рдХреЗ рд▓рд┐рдП рдкрд░реНрдпрд╛рдкреНрдд рдзрди рд╣реИ
    4. рдЖрдЬ рдХреЗ рд▓рд┐рдП рд╡реНрдпрдп рджреИрдирд┐рдХ рд╕реАрдорд╛ рд╕реЗ рдЕрдзрд┐рдХ рдирд╣реАрдВ рд╣реИред

  • рдЯреЙрдк рдЕрдк рдмреИрд▓реЗрдВрд╕


    рд╣рдо рд╕рдХреНрд░рд┐рдп рдФрд░ рдПрдХреНрд╕рдкрд╛рдпрд░реНрдб рдХрд╛рд░реНрдб рдХреЗ рд▓рд┐рдП рдмреИрд▓реЗрдВрд╕ рдирд╣реАрдВ рдмрдирд╛ рд╕рдХрддреЗ рд╣реИрдВред


  • рджреИрдирд┐рдХ рд╕реАрдорд╛ рдирд┐рд░реНрдзрд╛рд░рд┐рдд рдХрд░реЗрдВ


    рдпрджрд┐ рдХрд╛рд░реНрдб рдХреА рд╕рдордп рд╕реАрдорд╛ рд╕рдорд╛рдкреНрдд рдирд╣реАрдВ рд╣реБрдИ рд╣реИ рдФрд░ рд╕рдХреНрд░рд┐рдп рд╣реИ, рддреЛ рдЙрдкрдпреЛрдЧрдХрд░реНрддрд╛ рджреИрдирд┐рдХ рд╕реАрдорд╛ рдирд┐рд░реНрдзрд╛рд░рд┐рдд рдХрд░ рд╕рдХрддрд╛ рд╣реИред



рдЬрдм рдСрдкрд░реЗрд╢рди рдкреВрд░рд╛ рдирд╣реАрдВ рд╣реЛ рд╕рдХрддрд╛ рд╣реИ, рддреЛ рд╣рдореЗрдВ рдПрдХ рддреНрд░реБрдЯрд┐ рд╡рд╛рдкрд╕ рдХрд░рдиреА рд╣реЛрдЧреА, рдЗрд╕рд▓рд┐рдП рд╣рдореЗрдВ рдСрдкрд░реЗрд╢рдирдиреЛрдЯрдЕрд▓рдЧрд╛рд░реНрдбрд░ рдХреЛ рдкрд░рд┐рднрд╛рд╖рд┐рдд рдХрд░рдиреЗ рдХреА рдЖрд╡рд╢реНрдпрдХрддрд╛ рд╣реИ:


  type OperationNotAllowedError = { Operation: string Reason: string } //-- and a helper function to wrap it in `Error` which is a case for `Result<'ok,'error> type let operationNotAllowed operation reason = { Operation = operation; Reason = reason } |> Error 

рд╡реНрдпрд╛рдкрд╛рд░ рддрд░реНрдХ рдХреЗ рд╕рд╛рде рдЗрд╕ рдореЙрдбреНрдпреВрд▓ рдореЗрдВ рдПрдХ рд╣реА рдкреНрд░рдХрд╛рд░ рдХреА рддреНрд░реБрдЯрд┐ рд╣реЛрдЧреА рдЬреЛ рд╣рдо рд╡рд╛рдкрд╕ рдХрд░рддреЗ рд╣реИрдВред рд╣рдо рдпрд╣рд╛рдВ рд╕рддреНрдпрд╛рдкрди рдирд╣реАрдВ рдХрд░рддреЗ рд╣реИрдВ, рдбреЗрдЯрд╛рдмреЗрд╕ рдХреЗ рд╕рд╛рде рдмрд╛рддрдЪреАрдд рдирд╣реАрдВ рдХрд░рддреЗ рд╣реИрдВ - рдмрд╕ рд╕рдВрдЪрд╛рд▓рди рдХреЛ рдирд┐рд╖реНрдкрд╛рджрд┐рдд рдХрд░ рд░рд╣реЗ рд╣реИрдВ рдпрджрд┐ рд╣рдо рдЕрдиреНрдпрдерд╛ рдСрдкрд░реЗрд╢рдирдиреЛрдЯрдЕрд▓рд╛рдЗрдЧрдбрд╡рд░реНрдб рд╡рд╛рдкрд╕ рдХрд░ рд╕рдХрддреЗ рд╣реИрдВред


рдкреВрд░реНрдг рдореЙрдбреНрдпреВрд▓ рдпрд╣рд╛рдВ рдкрд╛рдпрд╛ рдЬрд╛ рд╕рдХрддрд╛ рд╣реИ ред рдореИрдВ рдпрд╣рд╛рдБ рдкреЗрдЪреАрджрд╛ рдорд╛рдорд▓рд╛ рдпрд╣рд╛рдБ рд╕реВрдЪреАрдмрджреНрдз рдХрд░реВрдБрдЧрд╛: processPayment ред рд╣рдореЗрдВ рд╕рдорд╛рдкреНрддрд┐, рд╕рдХреНрд░рд┐рдп / рдирд┐рд╖реНрдХреНрд░рд┐рдп рд╕реНрдерд┐рддрд┐, рдЖрдЬ рдЦрд░реНрдЪ рдХрд┐рдП рдЧрдП рдзрди рдФрд░ рд╡рд░реНрддрдорд╛рди рд╢реЗрд╖ рдХреЗ рд▓рд┐рдП рдЬрд╛рдВрдЪ рдХрд░рдиреА рд╣реЛрдЧреАред рдЪреВрдВрдХрд┐ рд╣рдо рдмрд╛рд╣рд░реА рджреБрдирд┐рдпрд╛ рдХреЗ рд╕рд╛рде рдмрд╛рддрдЪреАрдд рдирд╣реАрдВ рдХрд░ рд╕рдХрддреЗ рд╣реИрдВ, рдЗрд╕рд▓рд┐рдП рд╣рдореЗрдВ рд╕рднреА рдЖрд╡рд╢реНрдпрдХ рдЬрд╛рдирдХрд╛рд░реА рдХреЛ рдорд╛рдкрджрдВрдбреЛрдВ рдХреЗ рд░реВрдк рдореЗрдВ рдкрд╛рд░рд┐рдд рдХрд░рдирд╛ рд╣реЛрдЧрд╛ред рдЗрд╕ рддрд░рд╣ рдЗрд╕ рддрд░реНрдХ рдХрд╛ рдкрд░реАрдХреНрд╖рдг рдХрд░рдирд╛ рдмрд╣реБрдд рдЖрд╕рд╛рди рд╣реЛрдЧрд╛, рдФрд░ рдЖрдкрдХреЛ рд╕рдВрдкрддреНрддрд┐ рдЖрдзрд╛рд░рд┐рдд рдкрд░реАрдХреНрд╖рдг рдХрд░рдиреЗ рдХреА рдЕрдиреБрдорддрд┐ рдорд┐рд▓рддреА рд╣реИред


 let processPayment (currentDate: DateTimeOffset) (spentToday: Money) card (paymentAmount: MoneyTransaction) = //-- first check for expiration if isCardExpired currentDate card then cardExpiredMessage card.CardNumber |> processPaymentNotAllowed else //-- then active/deactivated match card.AccountDetails with | Deactivated -> cardDeactivatedMessage card.CardNumber |> processPaymentNotAllowed | Active accInfo -> //-- if active then check balance if paymentAmount.Value > accInfo.Balance.Value then sprintf "Insufficent funds on card %s" card.CardNumber.Value |> processPaymentNotAllowed else //-- if balance is ok check limit and money spent today match accInfo.DailyLimit with | Limit limit when limit < spentToday + paymentAmount -> sprintf "Daily limit is exceeded for card %s with daily limit %M. Today was spent %M" card.CardNumber.Value limit.Value spentToday.Value |> processPaymentNotAllowed (*{- We could use here the ultimate wild card case like this: | _ -> but it's dangerous because if a new case appears in `DailyLimit` type, we won't get a compile error here, which would remind us to process this new case in here. So this is a safe way to do the same thing. -}*) | Limit _ | Unlimited -> let newBalance = accInfo.Balance - paymentAmount let updatedCard = { card with AccountDetails = Active { accInfo with Balance = newBalance } } //-- note that we have to return balance operation, //-- so it can be stored to DB later. let balanceOperation = { Timestamp = currentDate CardNumber = card.CardNumber NewBalance = newBalance BalanceChange = Decrease paymentAmount } Ok (updatedCard, balanceOperation) 

рдпрд╣ spentToday - рд╣рдореЗрдВ рдЗрд╕реЗ рдбреЗрдЯрд╛рдмреЗрд╕ рдореЗрдВ рд░рдЦреЗ BalanceOperation рд╕рдВрдЧреНрд░рд╣ рд╕реЗ рдЧрдгрдирд╛ рдХрд░рдирд╛ рд╣реЛрдЧрд╛ред рддреЛ рд╣рдореЗрдВ рдЗрд╕рдХреЗ рд▓рд┐рдП рдореЙрдбреНрдпреВрд▓ рдХреА рдЖрд╡рд╢реНрдпрдХрддрд╛ рд╣реЛрдЧреА, рдЬрд┐рд╕рдореЗрдВ рдореВрд▓ рд░реВрдк рд╕реЗ 1 рд╕рд╛рд░реНрд╡рдЬрдирд┐рдХ рдлрд╝рдВрдХреНрд╢рди рд╣реЛрдЧрд╛:


  let private isDecrease change = match change with | Increase _ -> false | Decrease _ -> true let spentAtDate (date: DateTimeOffset) cardNumber operations = let date = date.Date let operationFilter { CardNumber = number; BalanceChange = change; Timestamp = timestamp } = isDecrease change && number = cardNumber && timestamp.Date = date let spendings = List.filter operationFilter operations List.sumBy (fun s -> -s.BalanceChange.ToDecimal()) spendings |> Money 

рдЕрдЪреНрдЫрд╛ред рдЕрдм рдЬрдм рд╣рдо рдорд╛рдирдЪрд┐рддреНрд░рдг рдХреЗ рдмрд╛рд░реЗ рдореЗрдВ рд╕реЛрдЪрдиреЗ рдХреЗ рд▓рд┐рдП рд╕рднреА рд╡реНрдпрд╛рд╡рд╕рд╛рдпрд┐рдХ рддрд░реНрдХ рдХрд╛рд░реНрдпрд╛рдиреНрд╡рдпрди, рд╕рдордп рдХреЗ рд╕рд╛рде рдХрд░ рд░рд╣реЗ рд╣реИрдВред рд╣рдорд╛рд░реЗ рдмрд╣реБрдд рд╕реЗ рдкреНрд░рдХрд╛рд░ рднреЗрджрднрд╛рд╡рдкреВрд░реНрдг рд╕рдВрдШреЛрдВ рдХрд╛ рдЙрдкрдпреЛрдЧ рдХрд░рддреЗ рд╣реИрдВ, рд╣рдорд╛рд░реЗ рдХреБрдЫ рдкреНрд░рдХрд╛рд░реЛрдВ рдХрд╛ рдХреЛрдИ рд╕рд╛рд░реНрд╡рдЬрдирд┐рдХ рдирд┐рд░реНрдорд╛рдгрдХрд░реНрддрд╛ рдирд╣реАрдВ рд╣реИ, рдЗрд╕рд▓рд┐рдП рд╣рдо рдЙрдиреНрд╣реЗрдВ рдмрд╛рд╣рд░ рдХреА рджреБрдирд┐рдпрд╛ рдХреЗ рд╕рдорд╛рди рдЙрдЬрд╛рдЧрд░ рдирд╣реАрдВ рдХрд░ рд╕рдХрддреЗред рд╣рдореЗрдВ (рдбреА) рдХреНрд░рдорд╛рдВрдХрди рд╕реЗ рдирд┐рдкрдЯрдирд╛ рд╣реЛрдЧрд╛ред рдЗрд╕рдХреЗ рдЕрд▓рд╛рд╡рд╛, рдЕрднреА рд╣рдорд╛рд░реЗ рдкрд╛рд╕ рд╣рдорд╛рд░реЗ рдЖрд╡реЗрджрди рдореЗрдВ рдХреЗрд╡рд▓ рдПрдХ рдмрд╛рдзреНрдп рд╕рдВрджрд░реНрдн рд╣реИ, рд▓реЗрдХрд┐рди рдмрд╛рдж рдореЗрдВ рд╡рд╛рд╕реНрддрд╡рд┐рдХ рдЬреАрд╡рди рдореЗрдВ рдЖрдк рдХрдИ рдмрдВрдзреЗ рд╣реБрдП рд╕рдВрджрд░реНрднреЛрдВ рдХреЗ рд╕рд╛рде рдПрдХ рдмрдбрд╝реА рдкреНрд░рдгрд╛рд▓реА рдХрд╛ рдирд┐рд░реНрдорд╛рдг рдХрд░рдирд╛ рдЪрд╛рд╣рддреЗ рд╣реИрдВ, рдФрд░ рдЙрдиреНрд╣реЗрдВ рд╕рд╛рд░реНрд╡рдЬрдирд┐рдХ рдЕрдиреБрдмрдВрдзреЛрдВ рдХреЗ рдорд╛рдзреНрдпрдо рд╕реЗ рдПрдХ-рджреВрд╕рд░реЗ рдХреЗ рд╕рд╛рде рдмрд╛рддрдЪреАрдд рдХрд░рдиреА рд╣реЛрдЧреА, рдЬреЛ рдХрд┐ рд╕рдордЭ рд╕реЗ рдмрд╛рд╣рд░ рд╣реЛрдирд╛ рдЪрд╛рд╣рд┐рдП рдЕрдиреНрдп рдкреНрд░реЛрдЧреНрд░рд╛рдорд┐рдВрдЧ рднрд╛рд╖рд╛рдУрдВ рд╕рд╣рд┐рдд рд╕рднреА рдХреЗ рд▓рд┐рдПред


рд╣рдореЗрдВ рджреЛрдиреЛрдВ рддрд░рд╣ рд╕реЗ рдореИрдкрд┐рдВрдЧ рдХрд░рдиреА рд╣реЛрдЧреА: рдкрдмреНрд▓рд┐рдХ рдореЙрдбрд▓ рд╕реЗ рдбреЛрдореЗрди рдФрд░ рд╡рд╛рдЗрд╕ рд╡рд░реНрд╕рд╛ рддрдХред рдЬрдмрдХрд┐ рдбреЛрдореЗрди рд╕реЗ рдореЙрдбрд▓ рдХреЗ рд▓рд┐рдП рдореИрдкрд┐рдВрдЧ рдмрд╣реБрдд рдЖрдЧреЗ рдХреА рдУрд░ рд╣реИ, рджреВрд╕рд░реА рджрд┐рд╢рд╛ рдореЗрдВ рдПрдХ рдЕрдЪрд╛рд░ рд╣реИ: рдореЙрдбрд▓ рдореЗрдВ рдЕрдорд╛рдиреНрдп рдбреЗрдЯрд╛ рд╣реЛ рд╕рдХрддрд╛ рд╣реИ, рдЖрдЦрд┐рд░рдХрд╛рд░ рд╣рдо рд╕рд╛рджреЗ рдкреНрд░рдХрд╛рд░реЛрдВ рдХрд╛ рдЙрдкрдпреЛрдЧ рдХрд░рддреЗ рд╣реИрдВ рдЬреЛ рдХрд┐ рдЬреАрдиреНрд╕ рдкрд░ рдХреНрд░рдордмрджреНрдз рд╣реЛ рд╕рдХрддреЗ рд╣реИрдВред рдЪрд┐рдВрддрд╛ рди рдХрд░реЗрдВ, рд╣рдореЗрдВ рдЙрд╕ рдорд╛рдирдЪрд┐рддреНрд░рдг рдореЗрдВ рдЕрдкрдиреА рдорд╛рдиреНрдпрддрд╛ рдмрдирд╛рдиреА рд╣реЛрдЧреАред рдпрд╣ рддрдереНрдп рдХрд┐ рд╣рдо рдЕрд╡реИрдз рдбреЗрдЯрд╛ рдФрд░ рдбреЗрдЯрд╛ рдХреЗ рд▓рд┐рдП рд╡рд┐рднрд┐рдиреНрди рдкреНрд░рдХрд╛рд░реЛрдВ рдХрд╛ рдЙрдкрдпреЛрдЧ рдХрд░рддреЗ рд╣реИрдВ, рдпрд╣ рд╣рдореЗрд╢рд╛ рд╡реИрдз рд╕рд╛рдзрди рд╣реИ, рдпрд╣ рд╕рдВрдХрд▓рдХ рд╣рдореЗрдВ рд╕рддреНрдпрд╛рдкрди рдирд┐рд╖реНрдкрд╛рджрд┐рдд рдХрд░рдиреЗ рдХреА рдЕрдиреБрдорддрд┐ рдирд╣реАрдВ рджреЗрдЧрд╛ред


рдпрд╣рд╛рдБ рдпрд╣ рдХреИрд╕рд╛ рджрд┐рдЦрддрд╛ рд╣реИ:


 (*{- You can use type aliases to annotate your functions. This is just an example, but sometimes it makes code more readable -}*) type ValidateCreateCardCommand = CreateCardCommandModel -> ValidationResult<Card> let validateCreateCardCommand : ValidateCreateCardCommand = fun cmd -> (*{- that's a computation expression for `Result<>` type. Thanks to this we don't have to chose between short code and strait forward one, like we have to do in C# -}*) result { let! name = LetterString.create "name" cmd.Name let! number = CardNumber.create "cardNumber" cmd.CardNumber let! month = Month.create "expirationMonth" cmd.ExpirationMonth let! year = Year.create "expirationYear" cmd.ExpirationYear return { Card.CardNumber = number Name = name HolderId = cmd.UserId Expiration = month,year AccountDetails = AccountInfo.Default cmd.UserId |> Active } } 

рдореИрдкрд┐рдВрдЧ рдФрд░ рдорд╛рдиреНрдпрддрд╛ рдХреЗ рд▓рд┐рдП рдкреВрд░реНрдг рдореЙрдбреНрдпреВрд▓ рдпрд╣рд╛рдВ рд╣реИ рдФрд░ рдореЙрдбрд▓ рдХреЗ рдорд╛рдирдЪрд┐рддреНрд░рдг рдХреЗ рд▓рд┐рдП рдореЙрдбреНрдпреВрд▓ рдпрд╣рд╛рдВ рд╣реИ ред


рдЗрд╕ рдмрд┐рдВрджреБ рдкрд░ рд╣рдорд╛рд░реЗ рдкрд╛рд╕ рд╕рднреА рд╡реНрдпрд╛рдкрд╛рд░рд┐рдХ рддрд░реНрдХ, рдореИрдкрд┐рдВрдЧ, рд╕рддреНрдпрд╛рдкрди рдФрд░ рдЗрддрдиреЗ рдкрд░ рдХрд╛рд░реНрдпрд╛рдиреНрд╡рдпрди рд╣реИ, рдФрд░ рдЕрдм рддрдХ рдпрд╣ рд╕рдм рд╡рд╛рд╕реНрддрд╡рд┐рдХ рджреБрдирд┐рдпрд╛ рд╕реЗ рдкреВрд░реА рддрд░рд╣ рд╕реЗ рдЕрд▓рдЧ рд╣реИ: рдпрд╣ рдкреВрд░реА рддрд░рд╣ рд╕реЗ рд╢реБрджреНрдз рдХрд╛рд░реНрдпреЛрдВ рдореЗрдВ рд▓рд┐рдЦрд╛ рдЧрдпрд╛ рд╣реИред рдЕрдм рдЖрдк рд╢рд╛рдпрдж рд╕реЛрдЪ рд░рд╣реЗ рд╣реЛрдВрдЧреЗ рдХрд┐ рд╣рдо рд╡рд╛рд╕реНрддрд╡ рдореЗрдВ рдЗрд╕рдХрд╛ рдЙрдкрдпреЛрдЧ рдХреИрд╕реЗ рдХрд░реЗрдВрдЧреЗ? рдХреНрдпреЛрдВрдХрд┐ рд╣рдореЗрдВ рдмрд╛рд╣рд░реА рджреБрдирд┐рдпрд╛ рд╕реЗ рдмрд╛рддрдЪреАрдд рдХрд░рдиреА рд╣реЛрдЧреАред рдЗрд╕рд╕реЗ рдЕрдзрд┐рдХ, рд╡рд░реНрдХрдлрд╝реНрд▓реЛ рдирд┐рд╖реНрдкрд╛рджрди рдХреЗ рджреМрд░рд╛рди рд╣рдореЗрдВ рдЙрди рд╡рд╛рд╕реНрддрд╡рд┐рдХ-рд╡рд┐рд╢реНрд╡ рдЗрдВрдЯрд░реИрдХреНрд╢рди рдХреЗ рдкрд░рд┐рдгрд╛рдо рдХреЗ рдЖрдзрд╛рд░ рдкрд░ рдХреБрдЫ рдирд┐рд░реНрдгрдп рд▓реЗрдиреЗ рд╣реЛрдВрдЧреЗред рддреЛ рд╕рд╡рд╛рд▓ рдпрд╣ рд╣реИ рдХрд┐ рд╣рдо рдЗрд╕ рд╕рдм рдХреЛ рдХреИрд╕реЗ рдЗрдХрдЯреНрдард╛ рдХрд░рддреЗ рд╣реИрдВ? OOP рдореЗрдВ рд╡реЗ рдЖрдИрдУрд╕реА рдХрдВрдЯреЗрдирд░реЛрдВ рдХрд╛ рдЙрдкрдпреЛрдЧ рдХрд░рддреЗ рд╣реИрдВ, рд▓реЗрдХрд┐рди рдЗрд╕рдХреА рджреЗрдЦрднрд╛рд▓ рдХреЗ рд▓рд┐рдП рд╣рдо рдпрд╣рд╛рдБ рдРрд╕рд╛ рдирд╣реАрдВ рдХрд░ рд╕рдХрддреЗ рд╣реИрдВ, рдХреНрдпреЛрдВрдХрд┐ рд╣рдорд╛рд░реЗ рдкрд╛рд╕ рд╡рд╕реНрддреБрдПрдВ рдирд╣реАрдВ рд╣реИрдВ, рд╣рдорд╛рд░реЗ рдкрд╛рд╕ рд╕реНрдерд┐рд░ рдХрд╛рд░реНрдп рд╣реИрдВред


рд╣рдо рдЙрд╕ рдХреЗ рд▓рд┐рдП Interpreter pattern рдЙрдкрдпреЛрдЧ рдХрд░рдиреЗ рд╡рд╛рд▓реЗ рд╣реИрдВ! рдпрд╣ рдереЛрдбрд╝рд╛ рдореБрд╢реНрдХрд┐рд▓ рд╣реИ, рдЬреНрдпрд╛рджрд╛рддрд░ рдЗрд╕рд▓рд┐рдП рдХрд┐ рдпрд╣ рдЕрдкрд░рд┐рдЪрд┐рдд рд╣реИ, рд▓реЗрдХрд┐рди рдореИрдВ рдЗрд╕ рдкреИрдЯрд░реНрди рдХреЛ рд╕рдордЭрд╛рдиреЗ рдХреА рдкреВрд░реА рдХреЛрд╢рд┐рд╢ рдХрд░реВрдВрдЧрд╛ред рд╕рдмрд╕реЗ рдкрд╣рд▓реЗ, рдлрд╝рдВрдХреНрд╢рди рд░рдЪрдирд╛ рдХреЗ рдмрд╛рд░реЗ рдореЗрдВ рдмрд╛рдд рдХрд░рддреЗ рд╣реИрдВред рдЙрджрд╛рд╣рд░рдг рдХреЗ рд▓рд┐рдП рд╣рдорд╛рд░реЗ рдкрд╛рд╕ рдПрдХ рдлрдВрдХреНрд╢рди int -> string ред рдЗрд╕рдХрд╛ рдорддрд▓рдм рд╣реИ рдХрд┐ рдлрд╝рдВрдХреНрд╢рди рдПрдХ рдкреИрд░рд╛рдореАрдЯрд░ рдХреЗ рд░реВрдк рдореЗрдВ int рдЕрдкреЗрдХреНрд╖рд╛ рдХрд░рддрд╛ рд╣реИ рдФрд░ рд╕реНрдЯреНрд░рд┐рдВрдЧ рд▓реМрдЯрд╛рддрд╛ рд╣реИред рдЕрдм рд╣рдо рдХрд╣рддреЗ рд╣реИрдВ рдХрд┐ рд╣рдорд╛рд░реЗ рдкрд╛рд╕ рдПрдХ рдФрд░ рдлрд╝рдВрдХреНрд╢рди string -> char ред рдЗрд╕ рдмрд┐рдВрджреБ рдкрд░ рд╣рдо рдЙрдиреНрд╣реЗрдВ рдЪреЗрди рдХрд░ рд╕рдХрддреЗ рд╣реИрдВ, рдпрд╛рдиреА рдкрд╣рд▓реЗ рдПрдХ рдХреЛ рдирд┐рд╖реНрдкрд╛рджрд┐рдд рдХрд░реЗрдВ, рдЗрд╕реЗ рдЖрдЙрдЯрдкреБрдЯ рд▓реЗрдВ рдФрд░ рдЗрд╕реЗ рджреВрд╕рд░реЗ рдлрд╝рдВрдХреНрд╢рди рдкрд░ рдлрд╝реАрдб рдХрд░реЗрдВ, рдФрд░ рдЗрд╕рдХреЗ рд▓рд┐рдП рдПрдХ рдСрдкрд░реЗрдЯрд░ рднреА рд╣реИ: >> рдпрд╣рд╛рдВ рдмрддрд╛рдпрд╛ рдЧрдпрд╛ рд╣реИ рдХрд┐ рдпрд╣ рдХреИрд╕реЗ рдХрд╛рдо рдХрд░рддрд╛ рд╣реИ:


 let intToString (i: int) = i.ToString() let firstCharOrSpace (s: string) = match s with | (null| "") -> ' ' | s -> s.[0] let firstDigitAsChar = intToString >> firstCharOrSpace //-- And you can chain as many functions as you like let alwaysTrue = intToString >> firstCharOrSpace >> Char.IsDigit 

рд╣рд╛рд▓рд╛рдБрдХрд┐ рд╣рдо рдХреБрдЫ рдкрд░рд┐рджреГрд╢реНрдпреЛрдВ рдореЗрдВ рд╕рд╛рдзрд╛рд░рдг рдЪреИрдирд┐рдВрдЧ рдХрд╛ рдЙрдкрдпреЛрдЧ рдирд╣реАрдВ рдХрд░ рд╕рдХрддреЗ рд╣реИрдВ, рдЬреИрд╕реЗ рдХрд┐ рдХрд╛рд░реНрдб рдХреЛ рд╕рдХреНрд░рд┐рдп рдХрд░рдирд╛ред рдпрд╣рд╛рдБ рдХреНрд░рд┐рдпрд╛рдУрдВ рдХрд╛ рдХреНрд░рдо рд╣реИ:


  • рдЗрдирдкреБрдЯ рдХрд╛рд░реНрдб рдирдВрдмрд░ рдорд╛рдиреНрдп рдХрд░реЗрдВред рдпрджрд┐ рдпрд╣ рдорд╛рдиреНрдп рд╣реИ, рддреЛ
  • рдЗрд╕ рд╕рдВрдЦреНрдпрд╛ рджреНрд╡рд╛рд░рд╛ рдХрд╛рд░реНрдб рдкреНрд░рд╛рдкреНрдд рдХрд░рдиреЗ рдХрд╛ рдкреНрд░рдпрд╛рд╕ рдХрд░реЗрдВред рдЕрдЧрд░ рдПрдХ рд╣реИ
  • рдЗрд╕реЗ рд╕рдХреНрд░рд┐рдп рдХрд░реЗрдВред
  • рдкрд░рд┐рдгрд╛рдо рд╕рд╣реЗрдЬреЗрдВред рдЕрдЧрд░ рдареАрдХ рд╣реИ рддреЛ
  • рдореЙрдбрд▓ рдХреЗ рд▓рд┐рдП рдирдХреНрд╢рд╛ рдФрд░ рд╡рд╛рдкрд╕реАред

рдкрд╣рд▓реЗ рджреЛ рдЪрд░рдгреЛрдВ рдореЗрдВ рдпрд╣ рд╣реИ рдХрд┐ If it's ok then... рдпрд╣реА рд╡рдЬрд╣ рд╣реИ рдХрд┐ рдбрд╛рдпрд░реЗрдХреНрдЯ рдЪреЗрдирд┐рдВрдЧ рдХрд╛рдо рдирд╣реАрдВ рдХрд░ рд░рд╣реА рд╣реИред


рд╣рдо рдмрд╕ рдЙрди рдХрд╛рд░реНрдпреЛрдВ рдХреЛ рдкреИрд░рд╛рдореАрдЯрд░ рдХреЗ рд░реВрдк рдореЗрдВ рдЗрдВрдЬреЗрдХреНрдЯ рдХрд░ рд╕рдХрддреЗ рд╣реИрдВ, рдЬреИрд╕реЗ:


 let activateCard getCardAsync saveCardAsync cardNumber = ... 

рд▓реЗрдХрд┐рди рдЙрд╕ рдХреЗ рд╕рд╛рде рдХреБрдЫ рд╕рдорд╕реНрдпрд╛рдПрдВ рд╣реИрдВред рд╕рдмрд╕реЗ рдкрд╣рд▓реЗ, рдирд┐рд░реНрднрд░рддрд╛ рдХреА рд╕рдВрдЦреНрдпрд╛ рдмрдбрд╝реА рд╣реЛ рд╕рдХрддреА рд╣реИ рдФрд░ рдлрд╝рдВрдХреНрд╢рди рд╣рд╕реНрддрд╛рдХреНрд╖рд░ рдмрджрд╕реВрд░рдд рджрд┐рдЦреЗрдВрдЧреЗред рджреВрд╕рд░рд╛, рд╣рдо рдпрд╣рд╛рдВ рд╡рд┐рд╢рд┐рд╖реНрдЯ рдкреНрд░рднрд╛рд╡реЛрдВ рд╕реЗ рдмрдВрдзреЗ рд╣реИрдВ: рд╣рдореЗрдВ рдпрд╣ рдЪреБрдирдирд╛ рд╣реИ рдХрд┐ рдХреНрдпрд╛ рдпрд╣ рдПрдХ Task рдпрд╛ Async рдпрд╛ рд╕рд┐рд░реНрдл рд╕рд╛рджрд╛ рд╕рд┐рдВрдХ рдХреЙрд▓ред рддреАрд╕рд░рд╛, рдЬрдм рдЖрдк рдкрд╛рд╕ рдХрд░рдиреЗ рдХреЗ рд▓рд┐рдП рдХрдИ рдХрд╛рд░реНрдп рдХрд░рддреЗ рд╣реИрдВ, рддреЛ рдЪреАрдЬреЛрдВ рдХреЛ createUserAsync : рдЙрджрд╛рд╣рд░рдг рдХреЗ рд▓рд┐рдП createUserAsync рдФрд░ replaceUserAsync рдкрд░ рдПрдХ рд╣реА рд╣рд╕реНрддрд╛рдХреНрд╖рд░ рд╣реЛрддреЗ рд╣реИрдВ, рд▓реЗрдХрд┐рди рдЕрд▓рдЧ-рдЕрд▓рдЧ рдкреНрд░рднрд╛рд╡ рд╣реЛрддреЗ рд╣реИрдВ, рдЗрд╕рд▓рд┐рдП рдЬрдм рдЖрдкрдХреЛ рдЙрдиреНрд╣реЗрдВ рд╕реИрдХрдбрд╝реЛрдВ рдмрд╛рд░ рдкрд╛рд╕ рдХрд░рдирд╛ рдкрдбрд╝рддрд╛ рд╣реИ рддреЛ рдЖрдк рд╡рд╛рд╕реНрддрд╡ рдореЗрдВ рдЕрдЬреАрдм рд▓рдХреНрд╖рдгреЛрдВ рдХреЗ рд╕рд╛рде рдЧрд▓рддреА рдХрд░ рд╕рдХрддреЗ рд╣реИрдВред рдЙрди рдХрд╛рд░рдгреЛрдВ рдХреЗ рдХрд╛рд░рдг рд╣рдо рджреБрднрд╛рд╖рд┐рдпрд╛ рдХреЗ рд▓рд┐рдП рдЬрд╛рддреЗ рд╣реИрдВред


рд╡рд┐рдЪрд╛рд░ рдпрд╣ рд╣реИ рдХрд┐ рд╣рдо рдЕрдкрдиреЗ рдХрдВрдкреЛрдЬрд┐рд╢рди рдХреЛрдб рдХреЛ 2 рднрд╛рдЧреЛрдВ рдореЗрдВ рд╡рд┐рднрд╛рдЬрд┐рдд рдХрд░рддреЗ рд╣реИрдВ: рдирд┐рд╖реНрдкрд╛рджрди рдЯреНрд░реА рдФрд░ рдЙрд╕ рдкреЗрдбрд╝ рдХреЗ рд▓рд┐рдП рджреБрднрд╛рд╖рд┐рдпрд╛ред рдЗрд╕ рдкреЗрдбрд╝ рдореЗрдВ рдкреНрд░рддреНрдпреЗрдХ рдиреЛрдб рдПрдХ рдлрд╝рдВрдХреНрд╢рди рдХреЗ рд▓рд┐рдП рдПрдХ рдЬрдЧрд╣ рд╣реИ рдЬрд┐рд╕рдХреЗ рдкреНрд░рднрд╛рд╡ рдХреЛ рд╣рдо рдЗрдВрдЬреЗрдХреНрдЯ рдХрд░рдирд╛ рдЪрд╛рд╣рддреЗ рд╣реИрдВ, рдЬреИрд╕реЗ getUserFromDatabase ред рдЙрди рдиреЛрдбреНрд╕ рдХреЛ рдирд╛рдо рд╕реЗ рдкрд░рд┐рднрд╛рд╖рд┐рдд рдХрд┐рдпрд╛ рдЬрд╛рддрд╛ рд╣реИ, рдЬреИрд╕реЗ getCard , рдЗрдирдкреБрдЯ рдкреИрд░рд╛рдореАрдЯрд░ рдкреНрд░рдХрд╛рд░, рдЬреИрд╕реЗ CardNumber рдФрд░ рд░рд┐рдЯрд░реНрди рдкреНрд░рдХрд╛рд░, рдЬреИрд╕реЗ Card option ред рд╣рдо рдпрд╣рд╛рдВ Task рдпрд╛ Async рдирд┐рд░реНрджрд┐рд╖реНрдЯ рдирд╣реАрдВ рдХрд░рддреЗ рд╣реИрдВ, рдпрд╣ рдкреЗрдбрд╝ рдХрд╛ рд╣рд┐рд╕реНрд╕рд╛ рдирд╣реАрдВ рд╣реИ, рдпрд╣ рджреБрднрд╛рд╖рд┐рдпрд╛ рдХрд╛ рдПрдХ рд╣рд┐рд╕реНрд╕рд╛ рд╣реИ ред рдЗрд╕ рдкреЗрдбрд╝ рдХрд╛ рд╣рд░ рдХрд┐рдирд╛рд░рд╛ рд╢реБрджреНрдз рдкрд░рд┐рд╡рд░реНрддрди рдХреА рдХреБрдЫ рд╢реНрд░реГрдВрдЦрд▓рд╛ рд╣реИ, рдЬреИрд╕реЗ рд╕рддреНрдпрд╛рдкрди рдпрд╛ рд╡реНрдпрд╡рд╕рд╛рдп рддрд░реНрдХ рдлрд╝рдВрдХреНрд╢рди рдирд┐рд╖реНрдкрд╛рджрдиред рдХрд┐рдирд╛рд░реЛрдВ рдореЗрдВ рдХреБрдЫ рдЗрдирдкреБрдЯ рднреА рд╣реЛрддреЗ рд╣реИрдВ, рдЬреИрд╕реЗ рдХрдЪреНрдЪреЗ рд╕реНрдЯреНрд░рд┐рдВрдЧ рдХрд╛рд░реНрдб рдирдВрдмрд░, рдлрд┐рд░ рд╕рддреНрдпрд╛рдкрди рд╣реЛрддрд╛ рд╣реИ, рдЬреЛ рд╣рдореЗрдВ рдПрдХ рддреНрд░реБрдЯрд┐ рдпрд╛ рд╡реИрдз рдХрд╛рд░реНрдб рдирдВрдмрд░ рджреЗ рд╕рдХрддрд╛ рд╣реИред рдпрджрд┐ рдХреЛрдИ рддреНрд░реБрдЯрд┐ рд╣реИ, рддреЛ рд╣рдо рдЙрд╕ рдХрд┐рдирд╛рд░реЗ рдХреЛ рдмрд╛рдзрд┐рдд рдХрд░рдиреЗ рд╡рд╛рд▓реЗ рд╣реИрдВ, рдпрджрд┐ рдирд╣реАрдВ, рддреЛ рдпрд╣ рд╣рдореЗрдВ рдЕрдЧрд▓реЗ рдиреЛрдб рддрдХ рд▓реЗ рдЬрд╛рддрд╛ рд╣реИ: getCard ред рдпрджрд┐ рдпрд╣ рдиреЛрдб Some card рд▓реМрдЯрд╛рдПрдЧрд╛, рддреЛ рд╣рдо рдЕрдЧрд▓реЗ рдХрд┐рдирд╛рд░реЗ рдкрд░ рдЬрд╛рд░реА рд░рдЦ рд╕рдХрддреЗ рд╣реИрдВ, рдЬреЛ рд╕рдХреНрд░рд┐рдп рд╣реЛрдЧрд╛, рдФрд░ рдЗрд╕реА рддрд░рд╣ред


topUp рдпрд╛ topUp рдпрд╛ topUp рдЬреИрд╕реЗ рд╣рд░ рдкрд░рд┐рджреГрд╢реНрдп рдХреЗ рд▓рд┐рдП рд╣рдо рдПрдХ рдЕрд▓рдЧ рдкреЗрдбрд╝ рдмрдирд╛рдиреЗ рдЬрд╛ рд░рд╣реЗ рд╣реИрдВред рдЬрдм рдЙрди рдкреЗрдбрд╝реЛрдВ рдХреЛ рдмрдирд╛рдпрд╛ рдЬрд╛рддрд╛ рд╣реИ, рддреЛ рдЙрдирдХреЗ рдиреЛрдб рдереЛрдбрд╝реЗ рдЦрд╛рд▓реА рд╣реЛрддреЗ рд╣реИрдВ, рдЙрдирдореЗрдВ рд╡рд╛рд╕реНрддрд╡рд┐рдХ рдХрд╛рд░реНрдп рдирд╣реАрдВ рд╣реЛрддреЗ рд╣реИрдВ, рдЙрди рдХрд╛рд░реНрдпреЛрдВ рдХреЗ рд▓рд┐рдП рдПрдХ рдЬрдЧрд╣ рд╣реЛрддреА рд╣реИред рджреБрднрд╛рд╖рд┐рдпрд╛ рдХрд╛ рд▓рдХреНрд╖реНрдп рдЙрди рдиреЛрдбреНрд╕ рдХреЛ рднрд░рдирд╛ рд╣реИ, рдЬреЛ рд╕рд░рд▓ рд╣реИред рджреБрднрд╛рд╖рд┐рдпрд╛ рд╣рдорд╛рд░реЗ рджреНрд╡рд╛рд░рд╛ рдЙрдкрдпреЛрдЧ рдХрд┐рдП рдЬрд╛рдиреЗ рд╡рд╛рд▓реЗ рдкреНрд░рднрд╛рд╡реЛрдВ рдХреЛ рдЬрд╛рдирддрд╛ рд╣реИ, рдЬреИрд╕реЗ Task , рдФрд░ рдпрд╣ рдЬрд╛рдирддрд╛ рд╣реИ рдХрд┐ рдХрд┐рд╕реА рджрд┐рдП рдЧрдП рдиреЛрдб рдореЗрдВ рдХреМрди рд╕рд╛ рд╡рд╛рд╕реНрддрд╡рд┐рдХ рдХрд╛рд░реНрдп рдХрд░рдирд╛ рд╣реИред рдЬрдм рдпрд╣ рдПрдХ рдиреЛрдб рдХрд╛ рджреМрд░рд╛ рдХрд░рддрд╛ рд╣реИ, рддреЛ рдпрд╣ рд╕рдВрдмрдВрдзрд┐рдд рд╡рд╛рд╕реНрддрд╡рд┐рдХ рдлрд╝рдВрдХреНрд╢рди рдХреЛ рдирд┐рд╖реНрдкрд╛рджрд┐рдд рдХрд░рддрд╛ рд╣реИ, рдЗрд╕реЗ Task рдпрд╛ Async рдорд╛рдорд▓реЗ рдореЗрдВ рдЗрдВрддрдЬрд╛рд░ рдХрд░рддрд╛ рд╣реИ, рдФрд░ рдкрд░рд┐рдгрд╛рдо рдХреЛ рдЕрдЧрд▓реЗ рдХрд┐рдирд╛рд░реЗ рдкрд░ рднреЗрдЬрддрд╛ рд╣реИред рдпрд╣ рдмрдврд╝рдд рджреВрд╕рд░реЗ рдиреЛрдб рдХреЛ рдЬрдиреНрдо рджреЗ рд╕рдХрддреА рд╣реИ, рдФрд░ рдлрд┐рд░ рдпрд╣ рджреБрднрд╛рд╖рд┐рдпрд╛ рдХреЗ рд▓рд┐рдП рдлрд┐рд░ рд╕реЗ рдПрдХ рдХрд╛рдо рд╣реИ, рдЬрдм рддрдХ рдХрд┐ рдпрд╣ рджреБрднрд╛рд╖рд┐рдпрд╛ рд╕реНрдЯреЙрдк рдиреЛрдб рддрдХ рдирд╣реАрдВ рдкрд╣реБрдВрдЪрддрд╛ рд╣реИ, рд╣рдорд╛рд░реА рдкреБрдирд░рд╛рд╡реГрддреНрддрд┐ рдХреЗ рдиреАрдЪреЗ, рдЬрд╣рд╛рдВ рд╣рдо рд╕рд┐рд░реНрдл рд╣рдорд╛рд░реЗ рдкреЗрдбрд╝ рдХреЗ рдкреВрд░реЗ рдирд┐рд╖реНрдкрд╛рджрди рдХрд╛ рдкрд░рд┐рдгрд╛рдо рджреЗрддреЗ рд╣реИрдВред


рдкреВрд░реЗ рдкреЗрдбрд╝ рдХреЛ рднреЗрджрднрд╛рд╡ рд╡рд╛рд▓реЗ рд╕рдВрдШ рдХреЗ рд╕рд╛рде рдкреНрд░рддрд┐рдирд┐рдзрд┐рддреНрд╡ рдХрд┐рдпрд╛ рдЬрд╛рдПрдЧрд╛, рдФрд░ рдПрдХ рдиреЛрдб рдЗрд╕ рддрд░рд╣ рджрд┐рдЦреЗрдЧрд╛:


  type Program<'a> = | GetCard of CardNumber * (Card option -> Program<'a>) //-- <- THE NODE | ... //-- ANOTHER NODE 

рдпрд╣ рд╣рдореЗрд╢рд╛ рдПрдХ рдЯрдкрд▓ рд╣реЛрдиреЗ рд╡рд╛рд▓рд╛ рд╣реИ, рдЬрд╣рд╛рдВ рдкрд╣рд▓рд╛ рддрддреНрд╡ рдЖрдкрдХреА рдирд┐рд░реНрднрд░рддрд╛ рдХреЗ рд▓рд┐рдП рдПрдХ рдЗрдирдкреБрдЯ рд╣реИ, рдФрд░ рдЕрдВрддрд┐рдо рддрддреНрд╡ рдПрдХ рдлрд╝рдВрдХреНрд╢рди рд╣реИ , рдЬреЛ рдЙрд╕ рдирд┐рд░реНрднрд░рддрд╛ рдХрд╛ рдкрд░рд┐рдгрд╛рдо рдкреНрд░рд╛рдкреНрдд рдХрд░рддрд╛ рд╣реИред рдЯрдкрд▓ рдХреЗ рдЙрди рддрддреНрд╡реЛрдВ рдХреЗ рдмреАрдЪ рд╡рд╣ "рд╕реНрдерд╛рди" рд╡рд╣ рдЬрдЧрд╣ рд╣реИ рдЬрд╣рд╛рдБ рдЖрдкрдХреА рдирд┐рд░реНрднрд░рддрд╛ рдлрд┐рдЯ рд╣реЛрдЧреА, рдЬреИрд╕реЗ рдЙрди рд░рдЪрдирд╛ рдЙрджрд╛рд╣рд░рдгреЛрдВ рдореЗрдВ, рдЬрд╣рд╛рдБ рдЖрдкрдХреЗ рдкрд╛рд╕ 'a -> 'b , 'c -> 'd рдФрд░ рдЖрдкрдХреЛ рдПрдХ рдФрд░ 'b -> 'c рд▓рдЧрд╛рдиреЗ рдХреА рдЖрд╡рд╢реНрдпрдХрддрд╛ рд╣реИред 'b -> 'c рдЙрдиреНрд╣реЗрдВ рдЬреЛрдбрд╝рдиреЗ рдХреЗ рд▓рд┐рдП рдмреАрдЪ рдореЗрдВ 'b -> 'c ред


рдЪреВрдБрдХрд┐ рд╣рдо рдЕрдкрдиреЗ рдмрдВрдзреЗ рд╣реБрдП рд╕рдВрджрд░реНрдн рдХреЗ рдЕрдВрджрд░ рд╣реИрдВ, рдЗрд╕рд▓рд┐рдП рд╣рдорд╛рд░реЗ рдкрд╛рд╕ рдмрд╣реБрдд рдЕрдзрд┐рдХ рдирд┐рд░реНрднрд░рддрд╛ рдирд╣реАрдВ рд╣реЛрдиреА рдЪрд╛рд╣рд┐рдП, рдФрд░ рдпрджрд┐ рд╣рдо рдХрд░рддреЗ рд╣реИрдВ - рддреЛ рдпрд╣ рд╢рд╛рдпрдж рд╣рдорд╛рд░реЗ рд╕рдВрджрд░реНрдн рдХреЛ рдЫреЛрдЯреЗ рд▓реЛрдЧреЛрдВ рдореЗрдВ рд╡рд┐рднрд╛рдЬрд┐рдд рдХрд░рдиреЗ рдХрд╛ рд╕рдордп рд╣реИред


рдпрд╣рд╛рдБ рдпрд╣ рдХреИрд╕рд╛ рджрд┐рдЦрддрд╛ рд╣реИ, рдкреВрд░реНрдг рд╕реНрд░реЛрдд рдпрд╣рд╛рдБ рд╣реИ :


  type Program<'a> = | GetCard of CardNumber * (Card option -> Program<'a>) | GetCardWithAccountInfo of CardNumber * ((Card*AccountInfo) option -> Program<'a>) | CreateCard of (Card*AccountInfo) * (Result<unit, DataRelatedError> -> Program<'a>) | ReplaceCard of Card * (Result<unit, DataRelatedError> -> Program<'a>) | GetUser of UserId * (User option -> Program<'a>) | CreateUser of UserInfo * (Result<unit, DataRelatedError> -> Program<'a>) | GetBalanceOperations of (CardNumber * DateTimeOffset * DateTimeOffset) * (BalanceOperation list -> Program<'a>) | SaveBalanceOperation of BalanceOperation * (Result<unit, DataRelatedError> -> Program<'a>) | Stop of 'a (*{- This bind function allows you to pass a continuation for current node of your expression tree the code is basically a boiler plate, as you can see. -}*) let rec bind f instruction = match instruction with | GetCard (x, next) -> GetCard (x, (next >> bind f)) | GetCardWithAccountInfo (x, next) -> GetCardWithAccountInfo (x, (next >> bind f)) | CreateCard (x, next) -> CreateCard (x, (next >> bind f)) | ReplaceCard (x, next) -> ReplaceCard (x, (next >> bind f)) | GetUser (x, next) -> GetUser (x,(next >> bind f)) | CreateUser (x, next) -> CreateUser (x,(next >> bind f)) | GetBalanceOperations (x, next) -> GetBalanceOperations (x,(next >> bind f)) | SaveBalanceOperation (x, next) -> SaveBalanceOperation (x,(next >> bind f)) | Stop x -> fx (*{- This is a set of basic functions. Use them in your expression tree builder to represent dependency call -}*) let stop x = Stop x let getCardByNumber number = GetCard (number, stop) let getCardWithAccountInfo number = GetCardWithAccountInfo (number, stop) let createNewCard (card, acc) = CreateCard ((card, acc), stop) let replaceCard card = ReplaceCard (card, stop) let getUserById id = GetUser (id, stop) let createNewUser user = CreateUser (user, stop) let getBalanceOperations (number, fromDate, toDate) = GetBalanceOperations ((number, fromDate, toDate), stop) let saveBalanceOperation op = SaveBalanceOperation (op, stop) 

With a help of computation expressions , we now have a very easy way to build our workflows without having to care about implementation of real-world interactions. We do that in CardWorkflow module :


 (*{- `program` is the name of our computation expression. In every `let!` binding we unwrap the result of operation, which can be either `Program<'a>` or `Program<Result<'a, Error>>`. What we unwrap would be of type 'a. If, however, an operation returns `Error`, we stop the execution at this very step and return it. The only thing we have to take care of is making sure that type of error is the same in every operation we call -}*) let processPayment (currentDate: DateTimeOffset, payment) = program { (*{- You can see these `expectValidationError` and `expectDataRelatedErrors` functions here. What they do is map different errors into `Error` type, since every execution branch must return the same type, in this case `Result<'a, Error>`. They also help you quickly understand what's going on in every line of code: validation, logic or calling external storage. -}*) let! cmd = validateProcessPaymentCommand payment |> expectValidationError let! card = tryGetCard cmd.CardNumber let today = currentDate.Date |> DateTimeOffset let tomorrow = currentDate.Date.AddDays 1. |> DateTimeOffset let! operations = getBalanceOperations (cmd.CardNumber, today, tomorrow) let spentToday = BalanceOperation.spentAtDate currentDate cmd.CardNumber operations let! (card, op) = CardActions.processPayment currentDate spentToday card cmd.PaymentAmount |> expectOperationNotAllowedError do! saveBalanceOperation op |> expectDataRelatedErrorProgram do! replaceCard card |> expectDataRelatedErrorProgram return card |> toCardInfoModel |> Ok } 

This module is the last thing we need to implement in business layer. Also, I've done some refactoring: I moved errors and common types to Common project . About time we moved on to implementing data access layer.


Data access layer


The design of entities in this layer may depend on our database or framework we use to interact with it. Therefore domain layer doesn't know anything about these entities, which means we have to take care of mapping to and from domain models in here. Which is quite convenient for consumers of our DAL API. For this application I've chosen MongoDB, not because it's a best choice for this kind of task, but because there're many examples of using SQL DBs already and I wanted to add something different. We are gonna use C# driver.


For the most part it's gonna be pretty strait forward, the only tricky moment is with Card . When it's active it has an AccountInfo inside, when it's not it doesn't. So we have to split it in two documents: CardEntity and CardAccountInfoEntity , so that deactivating card doesn't erase information about balance and daily limit.


Other than that we just gonna use primitive types instead of discriminated unions and types with built-in validation.


There're also few things we need to take care of, since we are using C# library:


  • Convert null s to Option<'a>
  • Catch expected exceptions and convert them to our errors and wrap it in Result<_,_>

We start with CardDomainEntities module , where we define our entities:


  [<CLIMutable>] type CardEntity = { [<BsonId>] CardNumber: string Name: string IsActive: bool ExpirationMonth: uint16 ExpirationYear: uint16 UserId: UserId } with //-- we're gonna need this in every entity for error messages member this.EntityId = this.CardNumber.ToString() (*{- we use this Id comparer quotation (F# alternative to C# Expression) for updating entity by id, since for different entities identifier has different name and type -}*) member this.IdComparer = <@ System.Func<_,_> (fun c -> c.CardNumber = this.CardNumber) @> 

Those fields EntityId and IdComparer we are gonna use with a help of SRTP . We'll define functions that will retrieve them from any type that has those fields define, without forcing every entity to implement some interface:


  let inline (|HasEntityId|) x = fun () -> (^a : (member EntityId: string) x) let inline entityId (HasEntityId f) = f() let inline (|HasIdComparer|) x = fun () -> (^a : (member IdComparer: Quotations.Expr<Func< ^a, bool>>) x) //-- We need to convert F# quotations to C# expressions //-- which C# mongo db driver understands. let inline idComparer (HasIdComparer id) = id() |> LeafExpressionConverter.QuotationToExpression |> unbox<Expression<Func<_,_>>> 

As for null and Option thing, since we use record types, F# compiler doesn't allow using null value, neither for assigning nor for comparison. At the same time record types are just another CLR types, so technically we can and will get a null value, thanks to C# and design of this library. We can solve this in 2 ways: use AllowNullLiteral attribute, or use Unchecked.defaultof<'a> . I went for the second choice since this null situation should be localized as much as possible:


  let isNullUnsafe (arg: 'a when 'a: not struct) = arg = Unchecked.defaultof<'a> //-- then we have this function to convert nulls to option, //-- therefore we limited this toxic null thing in here. let unsafeNullToOption a = if isNullUnsafe a then None else Some a 

In order to deal with expected exception for duplicate key, we use Active Patterns again:


  //-- First we define a function which checks, whether exception is about duplicate key let private isDuplicateKeyException (ex: Exception) = ex :? MongoWriteException && (ex :?> MongoWriteException).WriteError.Category = ServerErrorCategory.DuplicateKey //-- Then we have to check wrapping exceptions for this let rec private (|DuplicateKey|_|) (ex: Exception) = match ex with | :? MongoWriteException as ex when isDuplicateKeyException ex -> Some ex | :? MongoBulkWriteException as bex when bex.InnerException |> isDuplicateKeyException -> Some (bex.InnerException :?> MongoWriteException) | :? AggregateException as aex when aex.InnerException |> isDuplicateKeyException -> Some (aex.InnerException :?> MongoWriteException) | _ -> None //-- And here's the usage: let inline private executeInsertAsync (func: 'a -> Async<unit>) arg = async { try do! func(arg) return Ok () with | DuplicateKey ex -> return EntityAlreadyExists (arg.GetType().Name, (entityId arg)) |> Error } 

After mapping is implemented we have everything we need to assemble API for our data access layer , which looks like this:


  //-- `MongoDb` is a type alias for `IMongoDatabase` let replaceUserAsync (mongoDb: MongoDb) : ReplaceUserAsync = fun user -> user |> DomainToEntityMapping.mapUserToEntity |> CommandRepository.replaceUserAsync mongoDb let getUserInfoAsync (mongoDb: MongoDb) : GetUserInfoAsync = fun userId -> async { let! userInfo = QueryRepository.getUserInfoAsync mongoDb userId return userInfo |> Option.map EntityToDomainMapping.mapUserInfoEntity } 

The last moment I mention is when we do mapping Entity -> Domain , we have to instantiate types with built-in validation, so there can be validation errors. In this case we won't use Result<_,_> because if we've got invalid data in DB, it's a bug, not something we expect. So we just throw an exception. Other than that nothing really interesting is happening in here. The full source code of data access layer you'll find here .


Composition, logging and all the rest


As you remember, we're not gonna use DI framework, we went for interpreter pattern. If you want to know why, here's some reasons:


  • IoC container operates in runtime. So until you run your program you can't know that all the dependencies are satisfied.
  • It's a powerful tool which is very easy to abuse: you can do property injection, use lazy dependencies, and sometimes even some business logic can find it's way in dependency registering/resolving (yeah, I've witnessed it). All of that makes code maintaining extremely hard.

That means we need a place for that functionality. We could place it on a top level in our Web Api, but in my opinion it's not a best choice: right now we are dealing with only 1 bounded context, but if there's more, this global place with all the interpreters for each context will become cumbersome. Besides, there's single responsibility rule, and web api project should be responsible for web, right? So we create CardManagement.Infrastructure project .


Here we will do several things:


  • Composing our functionality
  • App configuration
  • Logging

If we had more than 1 context, app configuration and log configuration should be moved to global infrastructure project, and the only thing happening in this project would be assembling API for our bounded context, but in our case this separation is not necessary.


Let's get down to composition. We've built execution trees in our domain layer, now we have to interpret them. Every node in that tree represents some dependency call, in our case a call to database. If we had a need to interact with 3rd party api, that would be in here also. So our interpreter has to know how to handle every node in that tree, which is verified in compile time, thanks to <TreatWarningsAsErrors> setting. Here's what it looks like:


 (*{- Those `bindAsync (next >> interpretCardProgram mongoDb)` work pretty simple: we execute async function to the left of this expression, await that operation and pass the result to the next node, after which we interpret that node as well, until we reach the bottom of this recursion: `Stop a` node. -}*) let rec private interpretCardProgram mongoDb prog = match prog with | GetCard (cardNumber, next) -> cardNumber |> getCardAsync mongoDb |> bindAsync (next >> interpretCardProgram mongoDb) | GetCardWithAccountInfo (number, next) -> number |> getCardWithAccInfoAsync mongoDb |> bindAsync (next >> interpretCardProgram mongoDb) | CreateCard ((card,acc), next) -> (card, acc) |> createCardAsync mongoDb |> bindAsync (next >> interpretCardProgram mongoDb) | ReplaceCard (card, next) -> card |> replaceCardAsync mongoDb |> bindAsync (next >> interpretCardProgram mongoDb) | GetUser (id, next) -> getUserAsync mongoDb id |> bindAsync (next >> interpretCardProgram mongoDb) | CreateUser (user, next) -> user |> createUserAsync mongoDb |> bindAsync (next >> interpretCardProgram mongoDb) | GetBalanceOperations (request, next) -> getBalanceOperationsAsync mongoDb request |> bindAsync (next >> interpretCardProgram mongoDb) | SaveBalanceOperation (op, next) -> saveBalanceOperationAsync mongoDb op |> bindAsync (next >> interpretCardProgram mongoDb) | Stop a -> async.Return a let interpret prog = try let interpret = interpretCardProgram (getMongoDb()) interpret prog with | failure -> Bug failure |> Error |> async.Return 

Note that this interpreter is the place where we have this async thing. We can do another interpreter with Task or just a plain sync version of it. Now you're probably wondering, how we can cover this with unit-test, since familiar mock libraries ain't gonna help us. Well, it's easy: you have to make another interpreter. Here's what it can look like:


  type SaveResult = Result<unit, DataRelatedError> type TestInterpreterConfig = { GetCard: Card option GetCardWithAccountInfo: (Card*AccountInfo) option CreateCard: SaveResult ReplaceCard: SaveResult GetUser: User option CreateUser: SaveResult GetBalanceOperations: BalanceOperation list SaveBalanceOperation: SaveResult } let defaultConfig = { GetCard = Some card GetUser = Some user GetCardWithAccountInfo = (card, accountInfo) |> Some CreateCard = Ok() GetBalanceOperations = balanceOperations SaveBalanceOperation = Ok() ReplaceCard = Ok() CreateUser = Ok() } let testInject a = fun _ -> a let rec interpretCardProgram config (prog: Program<'a>) = match prog with | GetCard (cardNumber, next) -> cardNumber |> testInject config.GetCard |> (next >> interpretCardProgram config) | GetCardWithAccountInfo (number, next) -> number |> testInject config.GetCardWithAccountInfo |> (next >> interpretCardProgram config) | CreateCard ((card,acc), next) -> (card, acc) |> testInject config.CreateCard |> (next >> interpretCardProgram config) | ReplaceCard (card, next) -> card |> testInject config.ReplaceCard |> (next >> interpretCardProgram config) | GetUser (id, next) -> id |> testInject config.GetUser |> (next >> interpretCardProgram config) | CreateUser (user, next) -> user |> testInject config.CreateUser |> (next >> interpretCardProgram config) | GetBalanceOperations (request, next) -> testInject config.GetBalanceOperations request |> (next >> interpretCardProgram config) | SaveBalanceOperation (op, next) -> testInject config.SaveBalanceOperation op |> (next >> interpretCardProgram config) | Stop a -> a 

We've created TestInterpreterConfig which holds desired results of every operation we want to inject. You can easily change that config for every given test and then just run interpreter. This interpreter is sync, since there's no reason to bother with Task or Async .


There's nothing really tricky about the logging, but you can find it in this module . The approach is that we wrap the function in logging: we log function name, parameters and log result. If result is ok, it's info, if error it's a warning and if it's a Bug then it's an error. That's pretty much it.


One last thing is to make a facade, since we don't want to expose raw interpreter calls. Here's the whole thing:


  let createUser arg = arg |> (CardWorkflow.createUser >> CardProgramInterpreter.interpret |> logifyResultAsync "CardApi.createUser") let createCard arg = arg |> (CardWorkflow.createCard >> CardProgramInterpreter.interpret |> logifyResultAsync "CardApi.createCard") let activateCard arg = arg |> (CardWorkflow.activateCard >> CardProgramInterpreter.interpret |> logifyResultAsync "CardApi.activateCard") let deactivateCard arg = arg |> (CardWorkflow.deactivateCard >> CardProgramInterpreter.interpret |> logifyResultAsync "CardApi.deactivateCard") let processPayment arg = arg |> (CardWorkflow.processPayment >> CardProgramInterpreter.interpret |> logifyResultAsync "CardApi.processPayment") let topUp arg = arg |> (CardWorkflow.topUp >> CardProgramInterpreter.interpret |> logifyResultAsync "CardApi.topUp") let setDailyLimit arg = arg |> (CardWorkflow.setDailyLimit >> CardProgramInterpreter.interpret |> logifyResultAsync "CardApi.setDailyLimit") let getCard arg = arg |> (CardWorkflow.getCard >> CardProgramInterpreter.interpret |> logifyResultAsync "CardApi.getCard") let getUser arg = arg |> (CardWorkflow.getUser >> CardProgramInterpreter.interpretSimple |> logifyResultAsync "CardApi.getUser") 

All the dependencies here are injected, logging is taken care of, no exceptions is thrown тАФ that's it. For web api I used Giraffe framework. Web project is here .


рдирд┐рд╖реНрдХрд░реНрд╖


We have built an application with validation, error handling, logging, business logic тАФ all those things you usually have in your application. The difference is this code is way more durable and easy to refactor. Note that we haven't used reflection or code generation, no exceptions, but still our code isn't verbose. It's easy to read, easy to understand and hard to break. As soon as you add another field in your model, or another case in one of our union types, the code won't compile until you update every usage. Sure it doesn't mean you're totally safe or that you don't need any kind of testing at all, it just means that you're gonna have fewer problems when you develope new features or do some refactoring. The development process will be both cheaper and more interesting, because this tool allows you to focus on your domain and business tasks, instead of drugging focus on keeping an eye out that nothing is broken.


Another thing: I don't claim that OOP is completely useless and we don't need it, that's not true. I'm saying that we don't need it for solving every single task we have, and that a big portion of our tasks can be better solved with FP. And truth is, as always, in balance: we can't solve everything efficiently with only one tool, so a good programming language should have a decent support of both FP and OOP. And, unfortunately, a lot of most popular languages today have only lambdas and async programming from functional world.

Source: https://habr.com/ru/post/hi458730/


All Articles